roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11916      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19557             return;
19558         }
19559         var r = o.records, t = o.totalRecords || r.length;
19560         if(!options || options.add !== true){
19561             if(this.pruneModifiedRecords){
19562                 this.modified = [];
19563             }
19564             for(var i = 0, len = r.length; i < len; i++){
19565                 r[i].join(this);
19566             }
19567             if(this.snapshot){
19568                 this.data = this.snapshot;
19569                 delete this.snapshot;
19570             }
19571             this.data.clear();
19572             this.data.addAll(r);
19573             this.totalLength = t;
19574             this.applySort();
19575             this.fireEvent("datachanged", this);
19576         }else{
19577             this.totalLength = Math.max(t, this.data.length+r.length);
19578             this.add(r);
19579         }
19580         this.fireEvent("load", this, r, options);
19581         if(options.callback){
19582             options.callback.call(options.scope || this, r, options, true);
19583         }
19584     },
19585
19586     /**
19587      * Loads data from a passed data block. A Reader which understands the format of the data
19588      * must have been configured in the constructor.
19589      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19590      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19591      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19592      */
19593     loadData : function(o, append){
19594         var r = this.reader.readRecords(o);
19595         this.loadRecords(r, {add: append}, true);
19596     },
19597
19598     /**
19599      * Gets the number of cached records.
19600      * <p>
19601      * <em>If using paging, this may not be the total size of the dataset. If the data object
19602      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19603      * the data set size</em>
19604      */
19605     getCount : function(){
19606         return this.data.length || 0;
19607     },
19608
19609     /**
19610      * Gets the total number of records in the dataset as returned by the server.
19611      * <p>
19612      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19613      * the dataset size</em>
19614      */
19615     getTotalCount : function(){
19616         return this.totalLength || 0;
19617     },
19618
19619     /**
19620      * Returns the sort state of the Store as an object with two properties:
19621      * <pre><code>
19622  field {String} The name of the field by which the Records are sorted
19623  direction {String} The sort order, "ASC" or "DESC"
19624      * </code></pre>
19625      */
19626     getSortState : function(){
19627         return this.sortInfo;
19628     },
19629
19630     // private
19631     applySort : function(){
19632         if(this.sortInfo && !this.remoteSort){
19633             var s = this.sortInfo, f = s.field;
19634             var st = this.fields.get(f).sortType;
19635             var fn = function(r1, r2){
19636                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19637                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19638             };
19639             this.data.sort(s.direction, fn);
19640             if(this.snapshot && this.snapshot != this.data){
19641                 this.snapshot.sort(s.direction, fn);
19642             }
19643         }
19644     },
19645
19646     /**
19647      * Sets the default sort column and order to be used by the next load operation.
19648      * @param {String} fieldName The name of the field to sort by.
19649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19650      */
19651     setDefaultSort : function(field, dir){
19652         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19653     },
19654
19655     /**
19656      * Sort the Records.
19657      * If remote sorting is used, the sort is performed on the server, and the cache is
19658      * reloaded. If local sorting is used, the cache is sorted internally.
19659      * @param {String} fieldName The name of the field to sort by.
19660      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19661      */
19662     sort : function(fieldName, dir){
19663         var f = this.fields.get(fieldName);
19664         if(!dir){
19665             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19666             
19667             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19668                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19669             }else{
19670                 dir = f.sortDir;
19671             }
19672         }
19673         this.sortToggle[f.name] = dir;
19674         this.sortInfo = {field: f.name, direction: dir};
19675         if(!this.remoteSort){
19676             this.applySort();
19677             this.fireEvent("datachanged", this);
19678         }else{
19679             this.load(this.lastOptions);
19680         }
19681     },
19682
19683     /**
19684      * Calls the specified function for each of the Records in the cache.
19685      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19686      * Returning <em>false</em> aborts and exits the iteration.
19687      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19688      */
19689     each : function(fn, scope){
19690         this.data.each(fn, scope);
19691     },
19692
19693     /**
19694      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19695      * (e.g., during paging).
19696      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19697      */
19698     getModifiedRecords : function(){
19699         return this.modified;
19700     },
19701
19702     // private
19703     createFilterFn : function(property, value, anyMatch){
19704         if(!value.exec){ // not a regex
19705             value = String(value);
19706             if(value.length == 0){
19707                 return false;
19708             }
19709             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19710         }
19711         return function(r){
19712             return value.test(r.data[property]);
19713         };
19714     },
19715
19716     /**
19717      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19718      * @param {String} property A field on your records
19719      * @param {Number} start The record index to start at (defaults to 0)
19720      * @param {Number} end The last record index to include (defaults to length - 1)
19721      * @return {Number} The sum
19722      */
19723     sum : function(property, start, end){
19724         var rs = this.data.items, v = 0;
19725         start = start || 0;
19726         end = (end || end === 0) ? end : rs.length-1;
19727
19728         for(var i = start; i <= end; i++){
19729             v += (rs[i].data[property] || 0);
19730         }
19731         return v;
19732     },
19733
19734     /**
19735      * Filter the records by a specified property.
19736      * @param {String} field A field on your records
19737      * @param {String/RegExp} value Either a string that the field
19738      * should start with or a RegExp to test against the field
19739      * @param {Boolean} anyMatch True to match any part not just the beginning
19740      */
19741     filter : function(property, value, anyMatch){
19742         var fn = this.createFilterFn(property, value, anyMatch);
19743         return fn ? this.filterBy(fn) : this.clearFilter();
19744     },
19745
19746     /**
19747      * Filter by a function. The specified function will be called with each
19748      * record in this data source. If the function returns true the record is included,
19749      * otherwise it is filtered.
19750      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19751      * @param {Object} scope (optional) The scope of the function (defaults to this)
19752      */
19753     filterBy : function(fn, scope){
19754         this.snapshot = this.snapshot || this.data;
19755         this.data = this.queryBy(fn, scope||this);
19756         this.fireEvent("datachanged", this);
19757     },
19758
19759     /**
19760      * Query the records by a specified property.
19761      * @param {String} field A field on your records
19762      * @param {String/RegExp} value Either a string that the field
19763      * should start with or a RegExp to test against the field
19764      * @param {Boolean} anyMatch True to match any part not just the beginning
19765      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19766      */
19767     query : function(property, value, anyMatch){
19768         var fn = this.createFilterFn(property, value, anyMatch);
19769         return fn ? this.queryBy(fn) : this.data.clone();
19770     },
19771
19772     /**
19773      * Query by a function. The specified function will be called with each
19774      * record in this data source. If the function returns true the record is included
19775      * in the results.
19776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19777      * @param {Object} scope (optional) The scope of the function (defaults to this)
19778       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19779      **/
19780     queryBy : function(fn, scope){
19781         var data = this.snapshot || this.data;
19782         return data.filterBy(fn, scope||this);
19783     },
19784
19785     /**
19786      * Collects unique values for a particular dataIndex from this store.
19787      * @param {String} dataIndex The property to collect
19788      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19789      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19790      * @return {Array} An array of the unique values
19791      **/
19792     collect : function(dataIndex, allowNull, bypassFilter){
19793         var d = (bypassFilter === true && this.snapshot) ?
19794                 this.snapshot.items : this.data.items;
19795         var v, sv, r = [], l = {};
19796         for(var i = 0, len = d.length; i < len; i++){
19797             v = d[i].data[dataIndex];
19798             sv = String(v);
19799             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19800                 l[sv] = true;
19801                 r[r.length] = v;
19802             }
19803         }
19804         return r;
19805     },
19806
19807     /**
19808      * Revert to a view of the Record cache with no filtering applied.
19809      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19810      */
19811     clearFilter : function(suppressEvent){
19812         if(this.snapshot && this.snapshot != this.data){
19813             this.data = this.snapshot;
19814             delete this.snapshot;
19815             if(suppressEvent !== true){
19816                 this.fireEvent("datachanged", this);
19817             }
19818         }
19819     },
19820
19821     // private
19822     afterEdit : function(record){
19823         if(this.modified.indexOf(record) == -1){
19824             this.modified.push(record);
19825         }
19826         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19827     },
19828
19829     // private
19830     afterReject : function(record){
19831         this.modified.remove(record);
19832         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19833     },
19834
19835     // private
19836     afterCommit : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19839     },
19840
19841     /**
19842      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19843      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19844      */
19845     commitChanges : function(){
19846         var m = this.modified.slice(0);
19847         this.modified = [];
19848         for(var i = 0, len = m.length; i < len; i++){
19849             m[i].commit();
19850         }
19851     },
19852
19853     /**
19854      * Cancel outstanding changes on all changed records.
19855      */
19856     rejectChanges : function(){
19857         var m = this.modified.slice(0);
19858         this.modified = [];
19859         for(var i = 0, len = m.length; i < len; i++){
19860             m[i].reject();
19861         }
19862     },
19863
19864     onMetaChange : function(meta, rtype, o){
19865         this.recordType = rtype;
19866         this.fields = rtype.prototype.fields;
19867         delete this.snapshot;
19868         this.sortInfo = meta.sortInfo || this.sortInfo;
19869         this.modified = [];
19870         this.fireEvent('metachange', this, this.reader.meta);
19871     }
19872 });/*
19873  * Based on:
19874  * Ext JS Library 1.1.1
19875  * Copyright(c) 2006-2007, Ext JS, LLC.
19876  *
19877  * Originally Released Under LGPL - original licence link has changed is not relivant.
19878  *
19879  * Fork - LGPL
19880  * <script type="text/javascript">
19881  */
19882
19883 /**
19884  * @class Roo.data.SimpleStore
19885  * @extends Roo.data.Store
19886  * Small helper class to make creating Stores from Array data easier.
19887  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19888  * @cfg {Array} fields An array of field definition objects, or field name strings.
19889  * @cfg {Array} data The multi-dimensional array of data
19890  * @constructor
19891  * @param {Object} config
19892  */
19893 Roo.data.SimpleStore = function(config){
19894     Roo.data.SimpleStore.superclass.constructor.call(this, {
19895         isLocal : true,
19896         reader: new Roo.data.ArrayReader({
19897                 id: config.id
19898             },
19899             Roo.data.Record.create(config.fields)
19900         ),
19901         proxy : new Roo.data.MemoryProxy(config.data)
19902     });
19903     this.load();
19904 };
19905 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19906  * Based on:
19907  * Ext JS Library 1.1.1
19908  * Copyright(c) 2006-2007, Ext JS, LLC.
19909  *
19910  * Originally Released Under LGPL - original licence link has changed is not relivant.
19911  *
19912  * Fork - LGPL
19913  * <script type="text/javascript">
19914  */
19915
19916 /**
19917 /**
19918  * @extends Roo.data.Store
19919  * @class Roo.data.JsonStore
19920  * Small helper class to make creating Stores for JSON data easier. <br/>
19921 <pre><code>
19922 var store = new Roo.data.JsonStore({
19923     url: 'get-images.php',
19924     root: 'images',
19925     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19926 });
19927 </code></pre>
19928  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19929  * JsonReader and HttpProxy (unless inline data is provided).</b>
19930  * @cfg {Array} fields An array of field definition objects, or field name strings.
19931  * @constructor
19932  * @param {Object} config
19933  */
19934 Roo.data.JsonStore = function(c){
19935     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19936         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19937         reader: new Roo.data.JsonReader(c, c.fields)
19938     }));
19939 };
19940 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19941  * Based on:
19942  * Ext JS Library 1.1.1
19943  * Copyright(c) 2006-2007, Ext JS, LLC.
19944  *
19945  * Originally Released Under LGPL - original licence link has changed is not relivant.
19946  *
19947  * Fork - LGPL
19948  * <script type="text/javascript">
19949  */
19950
19951  
19952 Roo.data.Field = function(config){
19953     if(typeof config == "string"){
19954         config = {name: config};
19955     }
19956     Roo.apply(this, config);
19957     
19958     if(!this.type){
19959         this.type = "auto";
19960     }
19961     
19962     var st = Roo.data.SortTypes;
19963     // named sortTypes are supported, here we look them up
19964     if(typeof this.sortType == "string"){
19965         this.sortType = st[this.sortType];
19966     }
19967     
19968     // set default sortType for strings and dates
19969     if(!this.sortType){
19970         switch(this.type){
19971             case "string":
19972                 this.sortType = st.asUCString;
19973                 break;
19974             case "date":
19975                 this.sortType = st.asDate;
19976                 break;
19977             default:
19978                 this.sortType = st.none;
19979         }
19980     }
19981
19982     // define once
19983     var stripRe = /[\$,%]/g;
19984
19985     // prebuilt conversion function for this field, instead of
19986     // switching every time we're reading a value
19987     if(!this.convert){
19988         var cv, dateFormat = this.dateFormat;
19989         switch(this.type){
19990             case "":
19991             case "auto":
19992             case undefined:
19993                 cv = function(v){ return v; };
19994                 break;
19995             case "string":
19996                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19997                 break;
19998             case "int":
19999                 cv = function(v){
20000                     return v !== undefined && v !== null && v !== '' ?
20001                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20002                     };
20003                 break;
20004             case "float":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20008                     };
20009                 break;
20010             case "bool":
20011             case "boolean":
20012                 cv = function(v){ return v === true || v === "true" || v == 1; };
20013                 break;
20014             case "date":
20015                 cv = function(v){
20016                     if(!v){
20017                         return '';
20018                     }
20019                     if(v instanceof Date){
20020                         return v;
20021                     }
20022                     if(dateFormat){
20023                         if(dateFormat == "timestamp"){
20024                             return new Date(v*1000);
20025                         }
20026                         return Date.parseDate(v, dateFormat);
20027                     }
20028                     var parsed = Date.parse(v);
20029                     return parsed ? new Date(parsed) : null;
20030                 };
20031              break;
20032             
20033         }
20034         this.convert = cv;
20035     }
20036 };
20037
20038 Roo.data.Field.prototype = {
20039     dateFormat: null,
20040     defaultValue: "",
20041     mapping: null,
20042     sortType : null,
20043     sortDir : "ASC"
20044 };/*
20045  * Based on:
20046  * Ext JS Library 1.1.1
20047  * Copyright(c) 2006-2007, Ext JS, LLC.
20048  *
20049  * Originally Released Under LGPL - original licence link has changed is not relivant.
20050  *
20051  * Fork - LGPL
20052  * <script type="text/javascript">
20053  */
20054  
20055 // Base class for reading structured data from a data source.  This class is intended to be
20056 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20057
20058 /**
20059  * @class Roo.data.DataReader
20060  * Base class for reading structured data from a data source.  This class is intended to be
20061  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20062  */
20063
20064 Roo.data.DataReader = function(meta, recordType){
20065     
20066     this.meta = meta;
20067     
20068     this.recordType = recordType instanceof Array ? 
20069         Roo.data.Record.create(recordType) : recordType;
20070 };
20071
20072 Roo.data.DataReader.prototype = {
20073      /**
20074      * Create an empty record
20075      * @param {Object} data (optional) - overlay some values
20076      * @return {Roo.data.Record} record created.
20077      */
20078     newRow :  function(d) {
20079         var da =  {};
20080         this.recordType.prototype.fields.each(function(c) {
20081             switch( c.type) {
20082                 case 'int' : da[c.name] = 0; break;
20083                 case 'date' : da[c.name] = new Date(); break;
20084                 case 'float' : da[c.name] = 0.0; break;
20085                 case 'boolean' : da[c.name] = false; break;
20086                 default : da[c.name] = ""; break;
20087             }
20088             
20089         });
20090         return new this.recordType(Roo.apply(da, d));
20091     }
20092     
20093 };/*
20094  * Based on:
20095  * Ext JS Library 1.1.1
20096  * Copyright(c) 2006-2007, Ext JS, LLC.
20097  *
20098  * Originally Released Under LGPL - original licence link has changed is not relivant.
20099  *
20100  * Fork - LGPL
20101  * <script type="text/javascript">
20102  */
20103
20104 /**
20105  * @class Roo.data.DataProxy
20106  * @extends Roo.data.Observable
20107  * This class is an abstract base class for implementations which provide retrieval of
20108  * unformatted data objects.<br>
20109  * <p>
20110  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20111  * (of the appropriate type which knows how to parse the data object) to provide a block of
20112  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20113  * <p>
20114  * Custom implementations must implement the load method as described in
20115  * {@link Roo.data.HttpProxy#load}.
20116  */
20117 Roo.data.DataProxy = function(){
20118     this.addEvents({
20119         /**
20120          * @event beforeload
20121          * Fires before a network request is made to retrieve a data object.
20122          * @param {Object} This DataProxy object.
20123          * @param {Object} params The params parameter to the load function.
20124          */
20125         beforeload : true,
20126         /**
20127          * @event load
20128          * Fires before the load method's callback is called.
20129          * @param {Object} This DataProxy object.
20130          * @param {Object} o The data object.
20131          * @param {Object} arg The callback argument object passed to the load function.
20132          */
20133         load : true,
20134         /**
20135          * @event loadexception
20136          * Fires if an Exception occurs during data retrieval.
20137          * @param {Object} This DataProxy object.
20138          * @param {Object} o The data object.
20139          * @param {Object} arg The callback argument object passed to the load function.
20140          * @param {Object} e The Exception.
20141          */
20142         loadexception : true
20143     });
20144     Roo.data.DataProxy.superclass.constructor.call(this);
20145 };
20146
20147 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20148
20149     /**
20150      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20151      */
20152 /*
20153  * Based on:
20154  * Ext JS Library 1.1.1
20155  * Copyright(c) 2006-2007, Ext JS, LLC.
20156  *
20157  * Originally Released Under LGPL - original licence link has changed is not relivant.
20158  *
20159  * Fork - LGPL
20160  * <script type="text/javascript">
20161  */
20162 /**
20163  * @class Roo.data.MemoryProxy
20164  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20165  * to the Reader when its load method is called.
20166  * @constructor
20167  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20168  */
20169 Roo.data.MemoryProxy = function(data){
20170     if (data.data) {
20171         data = data.data;
20172     }
20173     Roo.data.MemoryProxy.superclass.constructor.call(this);
20174     this.data = data;
20175 };
20176
20177 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20178     /**
20179      * Load data from the requested source (in this case an in-memory
20180      * data object passed to the constructor), read the data object into
20181      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20182      * process that block using the passed callback.
20183      * @param {Object} params This parameter is not used by the MemoryProxy class.
20184      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20185      * object into a block of Roo.data.Records.
20186      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20187      * The function must be passed <ul>
20188      * <li>The Record block object</li>
20189      * <li>The "arg" argument from the load function</li>
20190      * <li>A boolean success indicator</li>
20191      * </ul>
20192      * @param {Object} scope The scope in which to call the callback
20193      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20194      */
20195     load : function(params, reader, callback, scope, arg){
20196         params = params || {};
20197         var result;
20198         try {
20199             result = reader.readRecords(this.data);
20200         }catch(e){
20201             this.fireEvent("loadexception", this, arg, null, e);
20202             callback.call(scope, null, arg, false);
20203             return;
20204         }
20205         callback.call(scope, result, arg, true);
20206     },
20207     
20208     // private
20209     update : function(params, records){
20210         
20211     }
20212 });/*
20213  * Based on:
20214  * Ext JS Library 1.1.1
20215  * Copyright(c) 2006-2007, Ext JS, LLC.
20216  *
20217  * Originally Released Under LGPL - original licence link has changed is not relivant.
20218  *
20219  * Fork - LGPL
20220  * <script type="text/javascript">
20221  */
20222 /**
20223  * @class Roo.data.HttpProxy
20224  * @extends Roo.data.DataProxy
20225  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20226  * configured to reference a certain URL.<br><br>
20227  * <p>
20228  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20229  * from which the running page was served.<br><br>
20230  * <p>
20231  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20232  * <p>
20233  * Be aware that to enable the browser to parse an XML document, the server must set
20234  * the Content-Type header in the HTTP response to "text/xml".
20235  * @constructor
20236  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20237  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20238  * will be used to make the request.
20239  */
20240 Roo.data.HttpProxy = function(conn){
20241     Roo.data.HttpProxy.superclass.constructor.call(this);
20242     // is conn a conn config or a real conn?
20243     this.conn = conn;
20244     this.useAjax = !conn || !conn.events;
20245   
20246 };
20247
20248 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20249     // thse are take from connection...
20250     
20251     /**
20252      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20253      */
20254     /**
20255      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20256      * extra parameters to each request made by this object. (defaults to undefined)
20257      */
20258     /**
20259      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20260      *  to each request made by this object. (defaults to undefined)
20261      */
20262     /**
20263      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20264      */
20265     /**
20266      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20267      */
20268      /**
20269      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20270      * @type Boolean
20271      */
20272   
20273
20274     /**
20275      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20276      * @type Boolean
20277      */
20278     /**
20279      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20280      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20281      * a finer-grained basis than the DataProxy events.
20282      */
20283     getConnection : function(){
20284         return this.useAjax ? Roo.Ajax : this.conn;
20285     },
20286
20287     /**
20288      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20289      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20290      * process that block using the passed callback.
20291      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20292      * for the request to the remote server.
20293      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20294      * object into a block of Roo.data.Records.
20295      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20296      * The function must be passed <ul>
20297      * <li>The Record block object</li>
20298      * <li>The "arg" argument from the load function</li>
20299      * <li>A boolean success indicator</li>
20300      * </ul>
20301      * @param {Object} scope The scope in which to call the callback
20302      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20303      */
20304     load : function(params, reader, callback, scope, arg){
20305         if(this.fireEvent("beforeload", this, params) !== false){
20306             var  o = {
20307                 params : params || {},
20308                 request: {
20309                     callback : callback,
20310                     scope : scope,
20311                     arg : arg
20312                 },
20313                 reader: reader,
20314                 callback : this.loadResponse,
20315                 scope: this
20316             };
20317             if(this.useAjax){
20318                 Roo.applyIf(o, this.conn);
20319                 if(this.activeRequest){
20320                     Roo.Ajax.abort(this.activeRequest);
20321                 }
20322                 this.activeRequest = Roo.Ajax.request(o);
20323             }else{
20324                 this.conn.request(o);
20325             }
20326         }else{
20327             callback.call(scope||this, null, arg, false);
20328         }
20329     },
20330
20331     // private
20332     loadResponse : function(o, success, response){
20333         delete this.activeRequest;
20334         if(!success){
20335             this.fireEvent("loadexception", this, o, response);
20336             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20337             return;
20338         }
20339         var result;
20340         try {
20341             result = o.reader.read(response);
20342         }catch(e){
20343             this.fireEvent("loadexception", this, o, response, e);
20344             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20345             return;
20346         }
20347         
20348         this.fireEvent("load", this, o, o.request.arg);
20349         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20350     },
20351
20352     // private
20353     update : function(dataSet){
20354
20355     },
20356
20357     // private
20358     updateResponse : function(dataSet){
20359
20360     }
20361 });/*
20362  * Based on:
20363  * Ext JS Library 1.1.1
20364  * Copyright(c) 2006-2007, Ext JS, LLC.
20365  *
20366  * Originally Released Under LGPL - original licence link has changed is not relivant.
20367  *
20368  * Fork - LGPL
20369  * <script type="text/javascript">
20370  */
20371
20372 /**
20373  * @class Roo.data.ScriptTagProxy
20374  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20375  * other than the originating domain of the running page.<br><br>
20376  * <p>
20377  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20378  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20379  * <p>
20380  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20381  * source code that is used as the source inside a &lt;script> tag.<br><br>
20382  * <p>
20383  * In order for the browser to process the returned data, the server must wrap the data object
20384  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20385  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20386  * depending on whether the callback name was passed:
20387  * <p>
20388  * <pre><code>
20389 boolean scriptTag = false;
20390 String cb = request.getParameter("callback");
20391 if (cb != null) {
20392     scriptTag = true;
20393     response.setContentType("text/javascript");
20394 } else {
20395     response.setContentType("application/x-json");
20396 }
20397 Writer out = response.getWriter();
20398 if (scriptTag) {
20399     out.write(cb + "(");
20400 }
20401 out.print(dataBlock.toJsonString());
20402 if (scriptTag) {
20403     out.write(");");
20404 }
20405 </pre></code>
20406  *
20407  * @constructor
20408  * @param {Object} config A configuration object.
20409  */
20410 Roo.data.ScriptTagProxy = function(config){
20411     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20412     Roo.apply(this, config);
20413     this.head = document.getElementsByTagName("head")[0];
20414 };
20415
20416 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20417
20418 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20419     /**
20420      * @cfg {String} url The URL from which to request the data object.
20421      */
20422     /**
20423      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20424      */
20425     timeout : 30000,
20426     /**
20427      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20428      * the server the name of the callback function set up by the load call to process the returned data object.
20429      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20430      * javascript output which calls this named function passing the data object as its only parameter.
20431      */
20432     callbackParam : "callback",
20433     /**
20434      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20435      * name to the request.
20436      */
20437     nocache : true,
20438
20439     /**
20440      * Load data from the configured URL, read the data object into
20441      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20442      * process that block using the passed callback.
20443      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20444      * for the request to the remote server.
20445      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20446      * object into a block of Roo.data.Records.
20447      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20448      * The function must be passed <ul>
20449      * <li>The Record block object</li>
20450      * <li>The "arg" argument from the load function</li>
20451      * <li>A boolean success indicator</li>
20452      * </ul>
20453      * @param {Object} scope The scope in which to call the callback
20454      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20455      */
20456     load : function(params, reader, callback, scope, arg){
20457         if(this.fireEvent("beforeload", this, params) !== false){
20458
20459             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20460
20461             var url = this.url;
20462             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20463             if(this.nocache){
20464                 url += "&_dc=" + (new Date().getTime());
20465             }
20466             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20467             var trans = {
20468                 id : transId,
20469                 cb : "stcCallback"+transId,
20470                 scriptId : "stcScript"+transId,
20471                 params : params,
20472                 arg : arg,
20473                 url : url,
20474                 callback : callback,
20475                 scope : scope,
20476                 reader : reader
20477             };
20478             var conn = this;
20479
20480             window[trans.cb] = function(o){
20481                 conn.handleResponse(o, trans);
20482             };
20483
20484             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20485
20486             if(this.autoAbort !== false){
20487                 this.abort();
20488             }
20489
20490             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20491
20492             var script = document.createElement("script");
20493             script.setAttribute("src", url);
20494             script.setAttribute("type", "text/javascript");
20495             script.setAttribute("id", trans.scriptId);
20496             this.head.appendChild(script);
20497
20498             this.trans = trans;
20499         }else{
20500             callback.call(scope||this, null, arg, false);
20501         }
20502     },
20503
20504     // private
20505     isLoading : function(){
20506         return this.trans ? true : false;
20507     },
20508
20509     /**
20510      * Abort the current server request.
20511      */
20512     abort : function(){
20513         if(this.isLoading()){
20514             this.destroyTrans(this.trans);
20515         }
20516     },
20517
20518     // private
20519     destroyTrans : function(trans, isLoaded){
20520         this.head.removeChild(document.getElementById(trans.scriptId));
20521         clearTimeout(trans.timeoutId);
20522         if(isLoaded){
20523             window[trans.cb] = undefined;
20524             try{
20525                 delete window[trans.cb];
20526             }catch(e){}
20527         }else{
20528             // if hasn't been loaded, wait for load to remove it to prevent script error
20529             window[trans.cb] = function(){
20530                 window[trans.cb] = undefined;
20531                 try{
20532                     delete window[trans.cb];
20533                 }catch(e){}
20534             };
20535         }
20536     },
20537
20538     // private
20539     handleResponse : function(o, trans){
20540         this.trans = false;
20541         this.destroyTrans(trans, true);
20542         var result;
20543         try {
20544             result = trans.reader.readRecords(o);
20545         }catch(e){
20546             this.fireEvent("loadexception", this, o, trans.arg, e);
20547             trans.callback.call(trans.scope||window, null, trans.arg, false);
20548             return;
20549         }
20550         this.fireEvent("load", this, o, trans.arg);
20551         trans.callback.call(trans.scope||window, result, trans.arg, true);
20552     },
20553
20554     // private
20555     handleFailure : function(trans){
20556         this.trans = false;
20557         this.destroyTrans(trans, false);
20558         this.fireEvent("loadexception", this, null, trans.arg);
20559         trans.callback.call(trans.scope||window, null, trans.arg, false);
20560     }
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571
20572 /**
20573  * @class Roo.data.JsonReader
20574  * @extends Roo.data.DataReader
20575  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20576  * based on mappings in a provided Roo.data.Record constructor.
20577  * 
20578  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20579  * in the reply previously. 
20580  * 
20581  * <p>
20582  * Example code:
20583  * <pre><code>
20584 var RecordDef = Roo.data.Record.create([
20585     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20586     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20587 ]);
20588 var myReader = new Roo.data.JsonReader({
20589     totalProperty: "results",    // The property which contains the total dataset size (optional)
20590     root: "rows",                // The property which contains an Array of row objects
20591     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20592 }, RecordDef);
20593 </code></pre>
20594  * <p>
20595  * This would consume a JSON file like this:
20596  * <pre><code>
20597 { 'results': 2, 'rows': [
20598     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20599     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20600 }
20601 </code></pre>
20602  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20603  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20604  * paged from the remote server.
20605  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20606  * @cfg {String} root name of the property which contains the Array of row objects.
20607  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20608  * @constructor
20609  * Create a new JsonReader
20610  * @param {Object} meta Metadata configuration options
20611  * @param {Object} recordType Either an Array of field definition objects,
20612  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20613  */
20614 Roo.data.JsonReader = function(meta, recordType){
20615     
20616     meta = meta || {};
20617     // set some defaults:
20618     Roo.applyIf(meta, {
20619         totalProperty: 'total',
20620         successProperty : 'success',
20621         root : 'data',
20622         id : 'id'
20623     });
20624     
20625     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20626 };
20627 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20628     
20629     /**
20630      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20631      * Used by Store query builder to append _requestMeta to params.
20632      * 
20633      */
20634     metaFromRemote : false,
20635     /**
20636      * This method is only used by a DataProxy which has retrieved data from a remote server.
20637      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20638      * @return {Object} data A data block which is used by an Roo.data.Store object as
20639      * a cache of Roo.data.Records.
20640      */
20641     read : function(response){
20642         var json = response.responseText;
20643        
20644         var o = /* eval:var:o */ eval("("+json+")");
20645         if(!o) {
20646             throw {message: "JsonReader.read: Json object not found"};
20647         }
20648         
20649         if(o.metaData){
20650             
20651             delete this.ef;
20652             this.metaFromRemote = true;
20653             this.meta = o.metaData;
20654             this.recordType = Roo.data.Record.create(o.metaData.fields);
20655             this.onMetaChange(this.meta, this.recordType, o);
20656         }
20657         return this.readRecords(o);
20658     },
20659
20660     // private function a store will implement
20661     onMetaChange : function(meta, recordType, o){
20662
20663     },
20664
20665     /**
20666          * @ignore
20667          */
20668     simpleAccess: function(obj, subsc) {
20669         return obj[subsc];
20670     },
20671
20672         /**
20673          * @ignore
20674          */
20675     getJsonAccessor: function(){
20676         var re = /[\[\.]/;
20677         return function(expr) {
20678             try {
20679                 return(re.test(expr))
20680                     ? new Function("obj", "return obj." + expr)
20681                     : function(obj){
20682                         return obj[expr];
20683                     };
20684             } catch(e){}
20685             return Roo.emptyFn;
20686         };
20687     }(),
20688
20689     /**
20690      * Create a data block containing Roo.data.Records from an XML document.
20691      * @param {Object} o An object which contains an Array of row objects in the property specified
20692      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20693      * which contains the total size of the dataset.
20694      * @return {Object} data A data block which is used by an Roo.data.Store object as
20695      * a cache of Roo.data.Records.
20696      */
20697     readRecords : function(o){
20698         /**
20699          * After any data loads, the raw JSON data is available for further custom processing.
20700          * @type Object
20701          */
20702         this.jsonData = o;
20703         var s = this.meta, Record = this.recordType,
20704             f = Record.prototype.fields, fi = f.items, fl = f.length;
20705
20706 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20707         if (!this.ef) {
20708             if(s.totalProperty) {
20709                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20710                 }
20711                 if(s.successProperty) {
20712                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20713                 }
20714                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20715                 if (s.id) {
20716                         var g = this.getJsonAccessor(s.id);
20717                         this.getId = function(rec) {
20718                                 var r = g(rec);
20719                                 return (r === undefined || r === "") ? null : r;
20720                         };
20721                 } else {
20722                         this.getId = function(){return null;};
20723                 }
20724             this.ef = [];
20725             for(var jj = 0; jj < fl; jj++){
20726                 f = fi[jj];
20727                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20728                 this.ef[jj] = this.getJsonAccessor(map);
20729             }
20730         }
20731
20732         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20733         if(s.totalProperty){
20734             var vt = parseInt(this.getTotal(o), 10);
20735             if(!isNaN(vt)){
20736                 totalRecords = vt;
20737             }
20738         }
20739         if(s.successProperty){
20740             var vs = this.getSuccess(o);
20741             if(vs === false || vs === 'false'){
20742                 success = false;
20743             }
20744         }
20745         var records = [];
20746             for(var i = 0; i < c; i++){
20747                     var n = root[i];
20748                 var values = {};
20749                 var id = this.getId(n);
20750                 for(var j = 0; j < fl; j++){
20751                     f = fi[j];
20752                 var v = this.ef[j](n);
20753                 if (!f.convert) {
20754                     Roo.log('missing convert for ' + f.name);
20755                     Roo.log(f);
20756                     continue;
20757                 }
20758                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20759                 }
20760                 var record = new Record(values, id);
20761                 record.json = n;
20762                 records[i] = record;
20763             }
20764             return {
20765                 success : success,
20766                 records : records,
20767                 totalRecords : totalRecords
20768             };
20769     }
20770 });/*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781 /**
20782  * @class Roo.data.XmlReader
20783  * @extends Roo.data.DataReader
20784  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20785  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20786  * <p>
20787  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20788  * header in the HTTP response must be set to "text/xml".</em>
20789  * <p>
20790  * Example code:
20791  * <pre><code>
20792 var RecordDef = Roo.data.Record.create([
20793    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20794    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20795 ]);
20796 var myReader = new Roo.data.XmlReader({
20797    totalRecords: "results", // The element which contains the total dataset size (optional)
20798    record: "row",           // The repeated element which contains row information
20799    id: "id"                 // The element within the row that provides an ID for the record (optional)
20800 }, RecordDef);
20801 </code></pre>
20802  * <p>
20803  * This would consume an XML file like this:
20804  * <pre><code>
20805 &lt;?xml?>
20806 &lt;dataset>
20807  &lt;results>2&lt;/results>
20808  &lt;row>
20809    &lt;id>1&lt;/id>
20810    &lt;name>Bill&lt;/name>
20811    &lt;occupation>Gardener&lt;/occupation>
20812  &lt;/row>
20813  &lt;row>
20814    &lt;id>2&lt;/id>
20815    &lt;name>Ben&lt;/name>
20816    &lt;occupation>Horticulturalist&lt;/occupation>
20817  &lt;/row>
20818 &lt;/dataset>
20819 </code></pre>
20820  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20821  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20822  * paged from the remote server.
20823  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20824  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20825  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20826  * a record identifier value.
20827  * @constructor
20828  * Create a new XmlReader
20829  * @param {Object} meta Metadata configuration options
20830  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20831  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20832  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20833  */
20834 Roo.data.XmlReader = function(meta, recordType){
20835     meta = meta || {};
20836     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20837 };
20838 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20839     /**
20840      * This method is only used by a DataProxy which has retrieved data from a remote server.
20841          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20842          * to contain a method called 'responseXML' that returns an XML document object.
20843      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20844      * a cache of Roo.data.Records.
20845      */
20846     read : function(response){
20847         var doc = response.responseXML;
20848         if(!doc) {
20849             throw {message: "XmlReader.read: XML Document not available"};
20850         }
20851         return this.readRecords(doc);
20852     },
20853
20854     /**
20855      * Create a data block containing Roo.data.Records from an XML document.
20856          * @param {Object} doc A parsed XML document.
20857      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20858      * a cache of Roo.data.Records.
20859      */
20860     readRecords : function(doc){
20861         /**
20862          * After any data loads/reads, the raw XML Document is available for further custom processing.
20863          * @type XMLDocument
20864          */
20865         this.xmlData = doc;
20866         var root = doc.documentElement || doc;
20867         var q = Roo.DomQuery;
20868         var recordType = this.recordType, fields = recordType.prototype.fields;
20869         var sid = this.meta.id;
20870         var totalRecords = 0, success = true;
20871         if(this.meta.totalRecords){
20872             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20873         }
20874         
20875         if(this.meta.success){
20876             var sv = q.selectValue(this.meta.success, root, true);
20877             success = sv !== false && sv !== 'false';
20878         }
20879         var records = [];
20880         var ns = q.select(this.meta.record, root);
20881         for(var i = 0, len = ns.length; i < len; i++) {
20882                 var n = ns[i];
20883                 var values = {};
20884                 var id = sid ? q.selectValue(sid, n) : undefined;
20885                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20886                     var f = fields.items[j];
20887                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20888                     v = f.convert(v);
20889                     values[f.name] = v;
20890                 }
20891                 var record = new recordType(values, id);
20892                 record.node = n;
20893                 records[records.length] = record;
20894             }
20895
20896             return {
20897                 success : success,
20898                 records : records,
20899                 totalRecords : totalRecords || records.length
20900             };
20901     }
20902 });/*
20903  * Based on:
20904  * Ext JS Library 1.1.1
20905  * Copyright(c) 2006-2007, Ext JS, LLC.
20906  *
20907  * Originally Released Under LGPL - original licence link has changed is not relivant.
20908  *
20909  * Fork - LGPL
20910  * <script type="text/javascript">
20911  */
20912
20913 /**
20914  * @class Roo.data.ArrayReader
20915  * @extends Roo.data.DataReader
20916  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20917  * Each element of that Array represents a row of data fields. The
20918  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20919  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20920  * <p>
20921  * Example code:.
20922  * <pre><code>
20923 var RecordDef = Roo.data.Record.create([
20924     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20925     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20926 ]);
20927 var myReader = new Roo.data.ArrayReader({
20928     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20929 }, RecordDef);
20930 </code></pre>
20931  * <p>
20932  * This would consume an Array like this:
20933  * <pre><code>
20934 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20935   </code></pre>
20936  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20937  * @constructor
20938  * Create a new JsonReader
20939  * @param {Object} meta Metadata configuration options.
20940  * @param {Object} recordType Either an Array of field definition objects
20941  * as specified to {@link Roo.data.Record#create},
20942  * or an {@link Roo.data.Record} object
20943  * created using {@link Roo.data.Record#create}.
20944  */
20945 Roo.data.ArrayReader = function(meta, recordType){
20946     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20947 };
20948
20949 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20950     /**
20951      * Create a data block containing Roo.data.Records from an XML document.
20952      * @param {Object} o An Array of row objects which represents the dataset.
20953      * @return {Object} data A data block which is used by an Roo.data.Store object as
20954      * a cache of Roo.data.Records.
20955      */
20956     readRecords : function(o){
20957         var sid = this.meta ? this.meta.id : null;
20958         var recordType = this.recordType, fields = recordType.prototype.fields;
20959         var records = [];
20960         var root = o;
20961             for(var i = 0; i < root.length; i++){
20962                     var n = root[i];
20963                 var values = {};
20964                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20965                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20966                 var f = fields.items[j];
20967                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20968                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20969                 v = f.convert(v);
20970                 values[f.name] = v;
20971             }
20972                 var record = new recordType(values, id);
20973                 record.json = n;
20974                 records[records.length] = record;
20975             }
20976             return {
20977                 records : records,
20978                 totalRecords : records.length
20979             };
20980     }
20981 });/*
20982  * Based on:
20983  * Ext JS Library 1.1.1
20984  * Copyright(c) 2006-2007, Ext JS, LLC.
20985  *
20986  * Originally Released Under LGPL - original licence link has changed is not relivant.
20987  *
20988  * Fork - LGPL
20989  * <script type="text/javascript">
20990  */
20991
20992
20993 /**
20994  * @class Roo.data.Tree
20995  * @extends Roo.util.Observable
20996  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20997  * in the tree have most standard DOM functionality.
20998  * @constructor
20999  * @param {Node} root (optional) The root node
21000  */
21001 Roo.data.Tree = function(root){
21002    this.nodeHash = {};
21003    /**
21004     * The root node for this tree
21005     * @type Node
21006     */
21007    this.root = null;
21008    if(root){
21009        this.setRootNode(root);
21010    }
21011    this.addEvents({
21012        /**
21013         * @event append
21014         * Fires when a new child node is appended to a node in this tree.
21015         * @param {Tree} tree The owner tree
21016         * @param {Node} parent The parent node
21017         * @param {Node} node The newly appended node
21018         * @param {Number} index The index of the newly appended node
21019         */
21020        "append" : true,
21021        /**
21022         * @event remove
21023         * Fires when a child node is removed from a node in this tree.
21024         * @param {Tree} tree The owner tree
21025         * @param {Node} parent The parent node
21026         * @param {Node} node The child node removed
21027         */
21028        "remove" : true,
21029        /**
21030         * @event move
21031         * Fires when a node is moved to a new location in the tree
21032         * @param {Tree} tree The owner tree
21033         * @param {Node} node The node moved
21034         * @param {Node} oldParent The old parent of this node
21035         * @param {Node} newParent The new parent of this node
21036         * @param {Number} index The index it was moved to
21037         */
21038        "move" : true,
21039        /**
21040         * @event insert
21041         * Fires when a new child node is inserted in a node in this tree.
21042         * @param {Tree} tree The owner tree
21043         * @param {Node} parent The parent node
21044         * @param {Node} node The child node inserted
21045         * @param {Node} refNode The child node the node was inserted before
21046         */
21047        "insert" : true,
21048        /**
21049         * @event beforeappend
21050         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21051         * @param {Tree} tree The owner tree
21052         * @param {Node} parent The parent node
21053         * @param {Node} node The child node to be appended
21054         */
21055        "beforeappend" : true,
21056        /**
21057         * @event beforeremove
21058         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21059         * @param {Tree} tree The owner tree
21060         * @param {Node} parent The parent node
21061         * @param {Node} node The child node to be removed
21062         */
21063        "beforeremove" : true,
21064        /**
21065         * @event beforemove
21066         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21067         * @param {Tree} tree The owner tree
21068         * @param {Node} node The node being moved
21069         * @param {Node} oldParent The parent of the node
21070         * @param {Node} newParent The new parent the node is moving to
21071         * @param {Number} index The index it is being moved to
21072         */
21073        "beforemove" : true,
21074        /**
21075         * @event beforeinsert
21076         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21077         * @param {Tree} tree The owner tree
21078         * @param {Node} parent The parent node
21079         * @param {Node} node The child node to be inserted
21080         * @param {Node} refNode The child node the node is being inserted before
21081         */
21082        "beforeinsert" : true
21083    });
21084
21085     Roo.data.Tree.superclass.constructor.call(this);
21086 };
21087
21088 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21089     pathSeparator: "/",
21090
21091     proxyNodeEvent : function(){
21092         return this.fireEvent.apply(this, arguments);
21093     },
21094
21095     /**
21096      * Returns the root node for this tree.
21097      * @return {Node}
21098      */
21099     getRootNode : function(){
21100         return this.root;
21101     },
21102
21103     /**
21104      * Sets the root node for this tree.
21105      * @param {Node} node
21106      * @return {Node}
21107      */
21108     setRootNode : function(node){
21109         this.root = node;
21110         node.ownerTree = this;
21111         node.isRoot = true;
21112         this.registerNode(node);
21113         return node;
21114     },
21115
21116     /**
21117      * Gets a node in this tree by its id.
21118      * @param {String} id
21119      * @return {Node}
21120      */
21121     getNodeById : function(id){
21122         return this.nodeHash[id];
21123     },
21124
21125     registerNode : function(node){
21126         this.nodeHash[node.id] = node;
21127     },
21128
21129     unregisterNode : function(node){
21130         delete this.nodeHash[node.id];
21131     },
21132
21133     toString : function(){
21134         return "[Tree"+(this.id?" "+this.id:"")+"]";
21135     }
21136 });
21137
21138 /**
21139  * @class Roo.data.Node
21140  * @extends Roo.util.Observable
21141  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21142  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21143  * @constructor
21144  * @param {Object} attributes The attributes/config for the node
21145  */
21146 Roo.data.Node = function(attributes){
21147     /**
21148      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21149      * @type {Object}
21150      */
21151     this.attributes = attributes || {};
21152     this.leaf = this.attributes.leaf;
21153     /**
21154      * The node id. @type String
21155      */
21156     this.id = this.attributes.id;
21157     if(!this.id){
21158         this.id = Roo.id(null, "ynode-");
21159         this.attributes.id = this.id;
21160     }
21161     /**
21162      * All child nodes of this node. @type Array
21163      */
21164     this.childNodes = [];
21165     if(!this.childNodes.indexOf){ // indexOf is a must
21166         this.childNodes.indexOf = function(o){
21167             for(var i = 0, len = this.length; i < len; i++){
21168                 if(this[i] == o) {
21169                     return i;
21170                 }
21171             }
21172             return -1;
21173         };
21174     }
21175     /**
21176      * The parent node for this node. @type Node
21177      */
21178     this.parentNode = null;
21179     /**
21180      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21181      */
21182     this.firstChild = null;
21183     /**
21184      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21185      */
21186     this.lastChild = null;
21187     /**
21188      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21189      */
21190     this.previousSibling = null;
21191     /**
21192      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21193      */
21194     this.nextSibling = null;
21195
21196     this.addEvents({
21197        /**
21198         * @event append
21199         * Fires when a new child node is appended
21200         * @param {Tree} tree The owner tree
21201         * @param {Node} this This node
21202         * @param {Node} node The newly appended node
21203         * @param {Number} index The index of the newly appended node
21204         */
21205        "append" : true,
21206        /**
21207         * @event remove
21208         * Fires when a child node is removed
21209         * @param {Tree} tree The owner tree
21210         * @param {Node} this This node
21211         * @param {Node} node The removed node
21212         */
21213        "remove" : true,
21214        /**
21215         * @event move
21216         * Fires when this node is moved to a new location in the tree
21217         * @param {Tree} tree The owner tree
21218         * @param {Node} this This node
21219         * @param {Node} oldParent The old parent of this node
21220         * @param {Node} newParent The new parent of this node
21221         * @param {Number} index The index it was moved to
21222         */
21223        "move" : true,
21224        /**
21225         * @event insert
21226         * Fires when a new child node is inserted.
21227         * @param {Tree} tree The owner tree
21228         * @param {Node} this This node
21229         * @param {Node} node The child node inserted
21230         * @param {Node} refNode The child node the node was inserted before
21231         */
21232        "insert" : true,
21233        /**
21234         * @event beforeappend
21235         * Fires before a new child is appended, return false to cancel the append.
21236         * @param {Tree} tree The owner tree
21237         * @param {Node} this This node
21238         * @param {Node} node The child node to be appended
21239         */
21240        "beforeappend" : true,
21241        /**
21242         * @event beforeremove
21243         * Fires before a child is removed, return false to cancel the remove.
21244         * @param {Tree} tree The owner tree
21245         * @param {Node} this This node
21246         * @param {Node} node The child node to be removed
21247         */
21248        "beforeremove" : true,
21249        /**
21250         * @event beforemove
21251         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21252         * @param {Tree} tree The owner tree
21253         * @param {Node} this This node
21254         * @param {Node} oldParent The parent of this node
21255         * @param {Node} newParent The new parent this node is moving to
21256         * @param {Number} index The index it is being moved to
21257         */
21258        "beforemove" : true,
21259        /**
21260         * @event beforeinsert
21261         * Fires before a new child is inserted, return false to cancel the insert.
21262         * @param {Tree} tree The owner tree
21263         * @param {Node} this This node
21264         * @param {Node} node The child node to be inserted
21265         * @param {Node} refNode The child node the node is being inserted before
21266         */
21267        "beforeinsert" : true
21268    });
21269     this.listeners = this.attributes.listeners;
21270     Roo.data.Node.superclass.constructor.call(this);
21271 };
21272
21273 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21274     fireEvent : function(evtName){
21275         // first do standard event for this node
21276         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21277             return false;
21278         }
21279         // then bubble it up to the tree if the event wasn't cancelled
21280         var ot = this.getOwnerTree();
21281         if(ot){
21282             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21283                 return false;
21284             }
21285         }
21286         return true;
21287     },
21288
21289     /**
21290      * Returns true if this node is a leaf
21291      * @return {Boolean}
21292      */
21293     isLeaf : function(){
21294         return this.leaf === true;
21295     },
21296
21297     // private
21298     setFirstChild : function(node){
21299         this.firstChild = node;
21300     },
21301
21302     //private
21303     setLastChild : function(node){
21304         this.lastChild = node;
21305     },
21306
21307
21308     /**
21309      * Returns true if this node is the last child of its parent
21310      * @return {Boolean}
21311      */
21312     isLast : function(){
21313        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21314     },
21315
21316     /**
21317      * Returns true if this node is the first child of its parent
21318      * @return {Boolean}
21319      */
21320     isFirst : function(){
21321        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21322     },
21323
21324     hasChildNodes : function(){
21325         return !this.isLeaf() && this.childNodes.length > 0;
21326     },
21327
21328     /**
21329      * Insert node(s) as the last child node of this node.
21330      * @param {Node/Array} node The node or Array of nodes to append
21331      * @return {Node} The appended node if single append, or null if an array was passed
21332      */
21333     appendChild : function(node){
21334         var multi = false;
21335         if(node instanceof Array){
21336             multi = node;
21337         }else if(arguments.length > 1){
21338             multi = arguments;
21339         }
21340         // if passed an array or multiple args do them one by one
21341         if(multi){
21342             for(var i = 0, len = multi.length; i < len; i++) {
21343                 this.appendChild(multi[i]);
21344             }
21345         }else{
21346             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21347                 return false;
21348             }
21349             var index = this.childNodes.length;
21350             var oldParent = node.parentNode;
21351             // it's a move, make sure we move it cleanly
21352             if(oldParent){
21353                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21354                     return false;
21355                 }
21356                 oldParent.removeChild(node);
21357             }
21358             index = this.childNodes.length;
21359             if(index == 0){
21360                 this.setFirstChild(node);
21361             }
21362             this.childNodes.push(node);
21363             node.parentNode = this;
21364             var ps = this.childNodes[index-1];
21365             if(ps){
21366                 node.previousSibling = ps;
21367                 ps.nextSibling = node;
21368             }else{
21369                 node.previousSibling = null;
21370             }
21371             node.nextSibling = null;
21372             this.setLastChild(node);
21373             node.setOwnerTree(this.getOwnerTree());
21374             this.fireEvent("append", this.ownerTree, this, node, index);
21375             if(oldParent){
21376                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21377             }
21378             return node;
21379         }
21380     },
21381
21382     /**
21383      * Removes a child node from this node.
21384      * @param {Node} node The node to remove
21385      * @return {Node} The removed node
21386      */
21387     removeChild : function(node){
21388         var index = this.childNodes.indexOf(node);
21389         if(index == -1){
21390             return false;
21391         }
21392         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21393             return false;
21394         }
21395
21396         // remove it from childNodes collection
21397         this.childNodes.splice(index, 1);
21398
21399         // update siblings
21400         if(node.previousSibling){
21401             node.previousSibling.nextSibling = node.nextSibling;
21402         }
21403         if(node.nextSibling){
21404             node.nextSibling.previousSibling = node.previousSibling;
21405         }
21406
21407         // update child refs
21408         if(this.firstChild == node){
21409             this.setFirstChild(node.nextSibling);
21410         }
21411         if(this.lastChild == node){
21412             this.setLastChild(node.previousSibling);
21413         }
21414
21415         node.setOwnerTree(null);
21416         // clear any references from the node
21417         node.parentNode = null;
21418         node.previousSibling = null;
21419         node.nextSibling = null;
21420         this.fireEvent("remove", this.ownerTree, this, node);
21421         return node;
21422     },
21423
21424     /**
21425      * Inserts the first node before the second node in this nodes childNodes collection.
21426      * @param {Node} node The node to insert
21427      * @param {Node} refNode The node to insert before (if null the node is appended)
21428      * @return {Node} The inserted node
21429      */
21430     insertBefore : function(node, refNode){
21431         if(!refNode){ // like standard Dom, refNode can be null for append
21432             return this.appendChild(node);
21433         }
21434         // nothing to do
21435         if(node == refNode){
21436             return false;
21437         }
21438
21439         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21440             return false;
21441         }
21442         var index = this.childNodes.indexOf(refNode);
21443         var oldParent = node.parentNode;
21444         var refIndex = index;
21445
21446         // when moving internally, indexes will change after remove
21447         if(oldParent == this && this.childNodes.indexOf(node) < index){
21448             refIndex--;
21449         }
21450
21451         // it's a move, make sure we move it cleanly
21452         if(oldParent){
21453             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21454                 return false;
21455             }
21456             oldParent.removeChild(node);
21457         }
21458         if(refIndex == 0){
21459             this.setFirstChild(node);
21460         }
21461         this.childNodes.splice(refIndex, 0, node);
21462         node.parentNode = this;
21463         var ps = this.childNodes[refIndex-1];
21464         if(ps){
21465             node.previousSibling = ps;
21466             ps.nextSibling = node;
21467         }else{
21468             node.previousSibling = null;
21469         }
21470         node.nextSibling = refNode;
21471         refNode.previousSibling = node;
21472         node.setOwnerTree(this.getOwnerTree());
21473         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21474         if(oldParent){
21475             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21476         }
21477         return node;
21478     },
21479
21480     /**
21481      * Returns the child node at the specified index.
21482      * @param {Number} index
21483      * @return {Node}
21484      */
21485     item : function(index){
21486         return this.childNodes[index];
21487     },
21488
21489     /**
21490      * Replaces one child node in this node with another.
21491      * @param {Node} newChild The replacement node
21492      * @param {Node} oldChild The node to replace
21493      * @return {Node} The replaced node
21494      */
21495     replaceChild : function(newChild, oldChild){
21496         this.insertBefore(newChild, oldChild);
21497         this.removeChild(oldChild);
21498         return oldChild;
21499     },
21500
21501     /**
21502      * Returns the index of a child node
21503      * @param {Node} node
21504      * @return {Number} The index of the node or -1 if it was not found
21505      */
21506     indexOf : function(child){
21507         return this.childNodes.indexOf(child);
21508     },
21509
21510     /**
21511      * Returns the tree this node is in.
21512      * @return {Tree}
21513      */
21514     getOwnerTree : function(){
21515         // if it doesn't have one, look for one
21516         if(!this.ownerTree){
21517             var p = this;
21518             while(p){
21519                 if(p.ownerTree){
21520                     this.ownerTree = p.ownerTree;
21521                     break;
21522                 }
21523                 p = p.parentNode;
21524             }
21525         }
21526         return this.ownerTree;
21527     },
21528
21529     /**
21530      * Returns depth of this node (the root node has a depth of 0)
21531      * @return {Number}
21532      */
21533     getDepth : function(){
21534         var depth = 0;
21535         var p = this;
21536         while(p.parentNode){
21537             ++depth;
21538             p = p.parentNode;
21539         }
21540         return depth;
21541     },
21542
21543     // private
21544     setOwnerTree : function(tree){
21545         // if it's move, we need to update everyone
21546         if(tree != this.ownerTree){
21547             if(this.ownerTree){
21548                 this.ownerTree.unregisterNode(this);
21549             }
21550             this.ownerTree = tree;
21551             var cs = this.childNodes;
21552             for(var i = 0, len = cs.length; i < len; i++) {
21553                 cs[i].setOwnerTree(tree);
21554             }
21555             if(tree){
21556                 tree.registerNode(this);
21557             }
21558         }
21559     },
21560
21561     /**
21562      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21563      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21564      * @return {String} The path
21565      */
21566     getPath : function(attr){
21567         attr = attr || "id";
21568         var p = this.parentNode;
21569         var b = [this.attributes[attr]];
21570         while(p){
21571             b.unshift(p.attributes[attr]);
21572             p = p.parentNode;
21573         }
21574         var sep = this.getOwnerTree().pathSeparator;
21575         return sep + b.join(sep);
21576     },
21577
21578     /**
21579      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21580      * function call will be the scope provided or the current node. The arguments to the function
21581      * will be the args provided or the current node. If the function returns false at any point,
21582      * the bubble is stopped.
21583      * @param {Function} fn The function to call
21584      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21585      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21586      */
21587     bubble : function(fn, scope, args){
21588         var p = this;
21589         while(p){
21590             if(fn.call(scope || p, args || p) === false){
21591                 break;
21592             }
21593             p = p.parentNode;
21594         }
21595     },
21596
21597     /**
21598      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21599      * function call will be the scope provided or the current node. The arguments to the function
21600      * will be the args provided or the current node. If the function returns false at any point,
21601      * the cascade is stopped on that branch.
21602      * @param {Function} fn The function to call
21603      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21604      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21605      */
21606     cascade : function(fn, scope, args){
21607         if(fn.call(scope || this, args || this) !== false){
21608             var cs = this.childNodes;
21609             for(var i = 0, len = cs.length; i < len; i++) {
21610                 cs[i].cascade(fn, scope, args);
21611             }
21612         }
21613     },
21614
21615     /**
21616      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21617      * function call will be the scope provided or the current node. The arguments to the function
21618      * will be the args provided or the current node. If the function returns false at any point,
21619      * the iteration stops.
21620      * @param {Function} fn The function to call
21621      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21622      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21623      */
21624     eachChild : function(fn, scope, args){
21625         var cs = this.childNodes;
21626         for(var i = 0, len = cs.length; i < len; i++) {
21627                 if(fn.call(scope || this, args || cs[i]) === false){
21628                     break;
21629                 }
21630         }
21631     },
21632
21633     /**
21634      * Finds the first child that has the attribute with the specified value.
21635      * @param {String} attribute The attribute name
21636      * @param {Mixed} value The value to search for
21637      * @return {Node} The found child or null if none was found
21638      */
21639     findChild : function(attribute, value){
21640         var cs = this.childNodes;
21641         for(var i = 0, len = cs.length; i < len; i++) {
21642                 if(cs[i].attributes[attribute] == value){
21643                     return cs[i];
21644                 }
21645         }
21646         return null;
21647     },
21648
21649     /**
21650      * Finds the first child by a custom function. The child matches if the function passed
21651      * returns true.
21652      * @param {Function} fn
21653      * @param {Object} scope (optional)
21654      * @return {Node} The found child or null if none was found
21655      */
21656     findChildBy : function(fn, scope){
21657         var cs = this.childNodes;
21658         for(var i = 0, len = cs.length; i < len; i++) {
21659                 if(fn.call(scope||cs[i], cs[i]) === true){
21660                     return cs[i];
21661                 }
21662         }
21663         return null;
21664     },
21665
21666     /**
21667      * Sorts this nodes children using the supplied sort function
21668      * @param {Function} fn
21669      * @param {Object} scope (optional)
21670      */
21671     sort : function(fn, scope){
21672         var cs = this.childNodes;
21673         var len = cs.length;
21674         if(len > 0){
21675             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21676             cs.sort(sortFn);
21677             for(var i = 0; i < len; i++){
21678                 var n = cs[i];
21679                 n.previousSibling = cs[i-1];
21680                 n.nextSibling = cs[i+1];
21681                 if(i == 0){
21682                     this.setFirstChild(n);
21683                 }
21684                 if(i == len-1){
21685                     this.setLastChild(n);
21686                 }
21687             }
21688         }
21689     },
21690
21691     /**
21692      * Returns true if this node is an ancestor (at any point) of the passed node.
21693      * @param {Node} node
21694      * @return {Boolean}
21695      */
21696     contains : function(node){
21697         return node.isAncestor(this);
21698     },
21699
21700     /**
21701      * Returns true if the passed node is an ancestor (at any point) of this node.
21702      * @param {Node} node
21703      * @return {Boolean}
21704      */
21705     isAncestor : function(node){
21706         var p = this.parentNode;
21707         while(p){
21708             if(p == node){
21709                 return true;
21710             }
21711             p = p.parentNode;
21712         }
21713         return false;
21714     },
21715
21716     toString : function(){
21717         return "[Node"+(this.id?" "+this.id:"")+"]";
21718     }
21719 });/*
21720  * Based on:
21721  * Ext JS Library 1.1.1
21722  * Copyright(c) 2006-2007, Ext JS, LLC.
21723  *
21724  * Originally Released Under LGPL - original licence link has changed is not relivant.
21725  *
21726  * Fork - LGPL
21727  * <script type="text/javascript">
21728  */
21729  
21730
21731 /**
21732  * @class Roo.ComponentMgr
21733  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21734  * @singleton
21735  */
21736 Roo.ComponentMgr = function(){
21737     var all = new Roo.util.MixedCollection();
21738
21739     return {
21740         /**
21741          * Registers a component.
21742          * @param {Roo.Component} c The component
21743          */
21744         register : function(c){
21745             all.add(c);
21746         },
21747
21748         /**
21749          * Unregisters a component.
21750          * @param {Roo.Component} c The component
21751          */
21752         unregister : function(c){
21753             all.remove(c);
21754         },
21755
21756         /**
21757          * Returns a component by id
21758          * @param {String} id The component id
21759          */
21760         get : function(id){
21761             return all.get(id);
21762         },
21763
21764         /**
21765          * Registers a function that will be called when a specified component is added to ComponentMgr
21766          * @param {String} id The component id
21767          * @param {Funtction} fn The callback function
21768          * @param {Object} scope The scope of the callback
21769          */
21770         onAvailable : function(id, fn, scope){
21771             all.on("add", function(index, o){
21772                 if(o.id == id){
21773                     fn.call(scope || o, o);
21774                     all.un("add", fn, scope);
21775                 }
21776             });
21777         }
21778     };
21779 }();/*
21780  * Based on:
21781  * Ext JS Library 1.1.1
21782  * Copyright(c) 2006-2007, Ext JS, LLC.
21783  *
21784  * Originally Released Under LGPL - original licence link has changed is not relivant.
21785  *
21786  * Fork - LGPL
21787  * <script type="text/javascript">
21788  */
21789  
21790 /**
21791  * @class Roo.Component
21792  * @extends Roo.util.Observable
21793  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21794  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21795  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21796  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21797  * All visual components (widgets) that require rendering into a layout should subclass Component.
21798  * @constructor
21799  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21800  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21801  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21802  */
21803 Roo.Component = function(config){
21804     config = config || {};
21805     if(config.tagName || config.dom || typeof config == "string"){ // element object
21806         config = {el: config, id: config.id || config};
21807     }
21808     this.initialConfig = config;
21809
21810     Roo.apply(this, config);
21811     this.addEvents({
21812         /**
21813          * @event disable
21814          * Fires after the component is disabled.
21815              * @param {Roo.Component} this
21816              */
21817         disable : true,
21818         /**
21819          * @event enable
21820          * Fires after the component is enabled.
21821              * @param {Roo.Component} this
21822              */
21823         enable : true,
21824         /**
21825          * @event beforeshow
21826          * Fires before the component is shown.  Return false to stop the show.
21827              * @param {Roo.Component} this
21828              */
21829         beforeshow : true,
21830         /**
21831          * @event show
21832          * Fires after the component is shown.
21833              * @param {Roo.Component} this
21834              */
21835         show : true,
21836         /**
21837          * @event beforehide
21838          * Fires before the component is hidden. Return false to stop the hide.
21839              * @param {Roo.Component} this
21840              */
21841         beforehide : true,
21842         /**
21843          * @event hide
21844          * Fires after the component is hidden.
21845              * @param {Roo.Component} this
21846              */
21847         hide : true,
21848         /**
21849          * @event beforerender
21850          * Fires before the component is rendered. Return false to stop the render.
21851              * @param {Roo.Component} this
21852              */
21853         beforerender : true,
21854         /**
21855          * @event render
21856          * Fires after the component is rendered.
21857              * @param {Roo.Component} this
21858              */
21859         render : true,
21860         /**
21861          * @event beforedestroy
21862          * Fires before the component is destroyed. Return false to stop the destroy.
21863              * @param {Roo.Component} this
21864              */
21865         beforedestroy : true,
21866         /**
21867          * @event destroy
21868          * Fires after the component is destroyed.
21869              * @param {Roo.Component} this
21870              */
21871         destroy : true
21872     });
21873     if(!this.id){
21874         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21875     }
21876     Roo.ComponentMgr.register(this);
21877     Roo.Component.superclass.constructor.call(this);
21878     this.initComponent();
21879     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21880         this.render(this.renderTo);
21881         delete this.renderTo;
21882     }
21883 };
21884
21885 /** @private */
21886 Roo.Component.AUTO_ID = 1000;
21887
21888 Roo.extend(Roo.Component, Roo.util.Observable, {
21889     /**
21890      * @scope Roo.Component.prototype
21891      * @type {Boolean}
21892      * true if this component is hidden. Read-only.
21893      */
21894     hidden : false,
21895     /**
21896      * @type {Boolean}
21897      * true if this component is disabled. Read-only.
21898      */
21899     disabled : false,
21900     /**
21901      * @type {Boolean}
21902      * true if this component has been rendered. Read-only.
21903      */
21904     rendered : false,
21905     
21906     /** @cfg {String} disableClass
21907      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21908      */
21909     disabledClass : "x-item-disabled",
21910         /** @cfg {Boolean} allowDomMove
21911          * Whether the component can move the Dom node when rendering (defaults to true).
21912          */
21913     allowDomMove : true,
21914     /** @cfg {String} hideMode
21915      * How this component should hidden. Supported values are
21916      * "visibility" (css visibility), "offsets" (negative offset position) and
21917      * "display" (css display) - defaults to "display".
21918      */
21919     hideMode: 'display',
21920
21921     /** @private */
21922     ctype : "Roo.Component",
21923
21924     /**
21925      * @cfg {String} actionMode 
21926      * which property holds the element that used for  hide() / show() / disable() / enable()
21927      * default is 'el' 
21928      */
21929     actionMode : "el",
21930
21931     /** @private */
21932     getActionEl : function(){
21933         return this[this.actionMode];
21934     },
21935
21936     initComponent : Roo.emptyFn,
21937     /**
21938      * If this is a lazy rendering component, render it to its container element.
21939      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21940      */
21941     render : function(container, position){
21942         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21943             if(!container && this.el){
21944                 this.el = Roo.get(this.el);
21945                 container = this.el.dom.parentNode;
21946                 this.allowDomMove = false;
21947             }
21948             this.container = Roo.get(container);
21949             this.rendered = true;
21950             if(position !== undefined){
21951                 if(typeof position == 'number'){
21952                     position = this.container.dom.childNodes[position];
21953                 }else{
21954                     position = Roo.getDom(position);
21955                 }
21956             }
21957             this.onRender(this.container, position || null);
21958             if(this.cls){
21959                 this.el.addClass(this.cls);
21960                 delete this.cls;
21961             }
21962             if(this.style){
21963                 this.el.applyStyles(this.style);
21964                 delete this.style;
21965             }
21966             this.fireEvent("render", this);
21967             this.afterRender(this.container);
21968             if(this.hidden){
21969                 this.hide();
21970             }
21971             if(this.disabled){
21972                 this.disable();
21973             }
21974         }
21975         return this;
21976     },
21977
21978     /** @private */
21979     // default function is not really useful
21980     onRender : function(ct, position){
21981         if(this.el){
21982             this.el = Roo.get(this.el);
21983             if(this.allowDomMove !== false){
21984                 ct.dom.insertBefore(this.el.dom, position);
21985             }
21986         }
21987     },
21988
21989     /** @private */
21990     getAutoCreate : function(){
21991         var cfg = typeof this.autoCreate == "object" ?
21992                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21993         if(this.id && !cfg.id){
21994             cfg.id = this.id;
21995         }
21996         return cfg;
21997     },
21998
21999     /** @private */
22000     afterRender : Roo.emptyFn,
22001
22002     /**
22003      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22004      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22005      */
22006     destroy : function(){
22007         if(this.fireEvent("beforedestroy", this) !== false){
22008             this.purgeListeners();
22009             this.beforeDestroy();
22010             if(this.rendered){
22011                 this.el.removeAllListeners();
22012                 this.el.remove();
22013                 if(this.actionMode == "container"){
22014                     this.container.remove();
22015                 }
22016             }
22017             this.onDestroy();
22018             Roo.ComponentMgr.unregister(this);
22019             this.fireEvent("destroy", this);
22020         }
22021     },
22022
22023         /** @private */
22024     beforeDestroy : function(){
22025
22026     },
22027
22028         /** @private */
22029         onDestroy : function(){
22030
22031     },
22032
22033     /**
22034      * Returns the underlying {@link Roo.Element}.
22035      * @return {Roo.Element} The element
22036      */
22037     getEl : function(){
22038         return this.el;
22039     },
22040
22041     /**
22042      * Returns the id of this component.
22043      * @return {String}
22044      */
22045     getId : function(){
22046         return this.id;
22047     },
22048
22049     /**
22050      * Try to focus this component.
22051      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22052      * @return {Roo.Component} this
22053      */
22054     focus : function(selectText){
22055         if(this.rendered){
22056             this.el.focus();
22057             if(selectText === true){
22058                 this.el.dom.select();
22059             }
22060         }
22061         return this;
22062     },
22063
22064     /** @private */
22065     blur : function(){
22066         if(this.rendered){
22067             this.el.blur();
22068         }
22069         return this;
22070     },
22071
22072     /**
22073      * Disable this component.
22074      * @return {Roo.Component} this
22075      */
22076     disable : function(){
22077         if(this.rendered){
22078             this.onDisable();
22079         }
22080         this.disabled = true;
22081         this.fireEvent("disable", this);
22082         return this;
22083     },
22084
22085         // private
22086     onDisable : function(){
22087         this.getActionEl().addClass(this.disabledClass);
22088         this.el.dom.disabled = true;
22089     },
22090
22091     /**
22092      * Enable this component.
22093      * @return {Roo.Component} this
22094      */
22095     enable : function(){
22096         if(this.rendered){
22097             this.onEnable();
22098         }
22099         this.disabled = false;
22100         this.fireEvent("enable", this);
22101         return this;
22102     },
22103
22104         // private
22105     onEnable : function(){
22106         this.getActionEl().removeClass(this.disabledClass);
22107         this.el.dom.disabled = false;
22108     },
22109
22110     /**
22111      * Convenience function for setting disabled/enabled by boolean.
22112      * @param {Boolean} disabled
22113      */
22114     setDisabled : function(disabled){
22115         this[disabled ? "disable" : "enable"]();
22116     },
22117
22118     /**
22119      * Show this component.
22120      * @return {Roo.Component} this
22121      */
22122     show: function(){
22123         if(this.fireEvent("beforeshow", this) !== false){
22124             this.hidden = false;
22125             if(this.rendered){
22126                 this.onShow();
22127             }
22128             this.fireEvent("show", this);
22129         }
22130         return this;
22131     },
22132
22133     // private
22134     onShow : function(){
22135         var ae = this.getActionEl();
22136         if(this.hideMode == 'visibility'){
22137             ae.dom.style.visibility = "visible";
22138         }else if(this.hideMode == 'offsets'){
22139             ae.removeClass('x-hidden');
22140         }else{
22141             ae.dom.style.display = "";
22142         }
22143     },
22144
22145     /**
22146      * Hide this component.
22147      * @return {Roo.Component} this
22148      */
22149     hide: function(){
22150         if(this.fireEvent("beforehide", this) !== false){
22151             this.hidden = true;
22152             if(this.rendered){
22153                 this.onHide();
22154             }
22155             this.fireEvent("hide", this);
22156         }
22157         return this;
22158     },
22159
22160     // private
22161     onHide : function(){
22162         var ae = this.getActionEl();
22163         if(this.hideMode == 'visibility'){
22164             ae.dom.style.visibility = "hidden";
22165         }else if(this.hideMode == 'offsets'){
22166             ae.addClass('x-hidden');
22167         }else{
22168             ae.dom.style.display = "none";
22169         }
22170     },
22171
22172     /**
22173      * Convenience function to hide or show this component by boolean.
22174      * @param {Boolean} visible True to show, false to hide
22175      * @return {Roo.Component} this
22176      */
22177     setVisible: function(visible){
22178         if(visible) {
22179             this.show();
22180         }else{
22181             this.hide();
22182         }
22183         return this;
22184     },
22185
22186     /**
22187      * Returns true if this component is visible.
22188      */
22189     isVisible : function(){
22190         return this.getActionEl().isVisible();
22191     },
22192
22193     cloneConfig : function(overrides){
22194         overrides = overrides || {};
22195         var id = overrides.id || Roo.id();
22196         var cfg = Roo.applyIf(overrides, this.initialConfig);
22197         cfg.id = id; // prevent dup id
22198         return new this.constructor(cfg);
22199     }
22200 });/*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210  (function(){ 
22211 /**
22212  * @class Roo.Layer
22213  * @extends Roo.Element
22214  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22215  * automatic maintaining of shadow/shim positions.
22216  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22217  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22218  * you can pass a string with a CSS class name. False turns off the shadow.
22219  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22220  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22221  * @cfg {String} cls CSS class to add to the element
22222  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22223  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22224  * @constructor
22225  * @param {Object} config An object with config options.
22226  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22227  */
22228
22229 Roo.Layer = function(config, existingEl){
22230     config = config || {};
22231     var dh = Roo.DomHelper;
22232     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22233     if(existingEl){
22234         this.dom = Roo.getDom(existingEl);
22235     }
22236     if(!this.dom){
22237         var o = config.dh || {tag: "div", cls: "x-layer"};
22238         this.dom = dh.append(pel, o);
22239     }
22240     if(config.cls){
22241         this.addClass(config.cls);
22242     }
22243     this.constrain = config.constrain !== false;
22244     this.visibilityMode = Roo.Element.VISIBILITY;
22245     if(config.id){
22246         this.id = this.dom.id = config.id;
22247     }else{
22248         this.id = Roo.id(this.dom);
22249     }
22250     this.zindex = config.zindex || this.getZIndex();
22251     this.position("absolute", this.zindex);
22252     if(config.shadow){
22253         this.shadowOffset = config.shadowOffset || 4;
22254         this.shadow = new Roo.Shadow({
22255             offset : this.shadowOffset,
22256             mode : config.shadow
22257         });
22258     }else{
22259         this.shadowOffset = 0;
22260     }
22261     this.useShim = config.shim !== false && Roo.useShims;
22262     this.useDisplay = config.useDisplay;
22263     this.hide();
22264 };
22265
22266 var supr = Roo.Element.prototype;
22267
22268 // shims are shared among layer to keep from having 100 iframes
22269 var shims = [];
22270
22271 Roo.extend(Roo.Layer, Roo.Element, {
22272
22273     getZIndex : function(){
22274         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22275     },
22276
22277     getShim : function(){
22278         if(!this.useShim){
22279             return null;
22280         }
22281         if(this.shim){
22282             return this.shim;
22283         }
22284         var shim = shims.shift();
22285         if(!shim){
22286             shim = this.createShim();
22287             shim.enableDisplayMode('block');
22288             shim.dom.style.display = 'none';
22289             shim.dom.style.visibility = 'visible';
22290         }
22291         var pn = this.dom.parentNode;
22292         if(shim.dom.parentNode != pn){
22293             pn.insertBefore(shim.dom, this.dom);
22294         }
22295         shim.setStyle('z-index', this.getZIndex()-2);
22296         this.shim = shim;
22297         return shim;
22298     },
22299
22300     hideShim : function(){
22301         if(this.shim){
22302             this.shim.setDisplayed(false);
22303             shims.push(this.shim);
22304             delete this.shim;
22305         }
22306     },
22307
22308     disableShadow : function(){
22309         if(this.shadow){
22310             this.shadowDisabled = true;
22311             this.shadow.hide();
22312             this.lastShadowOffset = this.shadowOffset;
22313             this.shadowOffset = 0;
22314         }
22315     },
22316
22317     enableShadow : function(show){
22318         if(this.shadow){
22319             this.shadowDisabled = false;
22320             this.shadowOffset = this.lastShadowOffset;
22321             delete this.lastShadowOffset;
22322             if(show){
22323                 this.sync(true);
22324             }
22325         }
22326     },
22327
22328     // private
22329     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22330     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22331     sync : function(doShow){
22332         var sw = this.shadow;
22333         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22334             var sh = this.getShim();
22335
22336             var w = this.getWidth(),
22337                 h = this.getHeight();
22338
22339             var l = this.getLeft(true),
22340                 t = this.getTop(true);
22341
22342             if(sw && !this.shadowDisabled){
22343                 if(doShow && !sw.isVisible()){
22344                     sw.show(this);
22345                 }else{
22346                     sw.realign(l, t, w, h);
22347                 }
22348                 if(sh){
22349                     if(doShow){
22350                        sh.show();
22351                     }
22352                     // fit the shim behind the shadow, so it is shimmed too
22353                     var a = sw.adjusts, s = sh.dom.style;
22354                     s.left = (Math.min(l, l+a.l))+"px";
22355                     s.top = (Math.min(t, t+a.t))+"px";
22356                     s.width = (w+a.w)+"px";
22357                     s.height = (h+a.h)+"px";
22358                 }
22359             }else if(sh){
22360                 if(doShow){
22361                    sh.show();
22362                 }
22363                 sh.setSize(w, h);
22364                 sh.setLeftTop(l, t);
22365             }
22366             
22367         }
22368     },
22369
22370     // private
22371     destroy : function(){
22372         this.hideShim();
22373         if(this.shadow){
22374             this.shadow.hide();
22375         }
22376         this.removeAllListeners();
22377         var pn = this.dom.parentNode;
22378         if(pn){
22379             pn.removeChild(this.dom);
22380         }
22381         Roo.Element.uncache(this.id);
22382     },
22383
22384     remove : function(){
22385         this.destroy();
22386     },
22387
22388     // private
22389     beginUpdate : function(){
22390         this.updating = true;
22391     },
22392
22393     // private
22394     endUpdate : function(){
22395         this.updating = false;
22396         this.sync(true);
22397     },
22398
22399     // private
22400     hideUnders : function(negOffset){
22401         if(this.shadow){
22402             this.shadow.hide();
22403         }
22404         this.hideShim();
22405     },
22406
22407     // private
22408     constrainXY : function(){
22409         if(this.constrain){
22410             var vw = Roo.lib.Dom.getViewWidth(),
22411                 vh = Roo.lib.Dom.getViewHeight();
22412             var s = Roo.get(document).getScroll();
22413
22414             var xy = this.getXY();
22415             var x = xy[0], y = xy[1];   
22416             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22417             // only move it if it needs it
22418             var moved = false;
22419             // first validate right/bottom
22420             if((x + w) > vw+s.left){
22421                 x = vw - w - this.shadowOffset;
22422                 moved = true;
22423             }
22424             if((y + h) > vh+s.top){
22425                 y = vh - h - this.shadowOffset;
22426                 moved = true;
22427             }
22428             // then make sure top/left isn't negative
22429             if(x < s.left){
22430                 x = s.left;
22431                 moved = true;
22432             }
22433             if(y < s.top){
22434                 y = s.top;
22435                 moved = true;
22436             }
22437             if(moved){
22438                 if(this.avoidY){
22439                     var ay = this.avoidY;
22440                     if(y <= ay && (y+h) >= ay){
22441                         y = ay-h-5;   
22442                     }
22443                 }
22444                 xy = [x, y];
22445                 this.storeXY(xy);
22446                 supr.setXY.call(this, xy);
22447                 this.sync();
22448             }
22449         }
22450     },
22451
22452     isVisible : function(){
22453         return this.visible;    
22454     },
22455
22456     // private
22457     showAction : function(){
22458         this.visible = true; // track visibility to prevent getStyle calls
22459         if(this.useDisplay === true){
22460             this.setDisplayed("");
22461         }else if(this.lastXY){
22462             supr.setXY.call(this, this.lastXY);
22463         }else if(this.lastLT){
22464             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22465         }
22466     },
22467
22468     // private
22469     hideAction : function(){
22470         this.visible = false;
22471         if(this.useDisplay === true){
22472             this.setDisplayed(false);
22473         }else{
22474             this.setLeftTop(-10000,-10000);
22475         }
22476     },
22477
22478     // overridden Element method
22479     setVisible : function(v, a, d, c, e){
22480         if(v){
22481             this.showAction();
22482         }
22483         if(a && v){
22484             var cb = function(){
22485                 this.sync(true);
22486                 if(c){
22487                     c();
22488                 }
22489             }.createDelegate(this);
22490             supr.setVisible.call(this, true, true, d, cb, e);
22491         }else{
22492             if(!v){
22493                 this.hideUnders(true);
22494             }
22495             var cb = c;
22496             if(a){
22497                 cb = function(){
22498                     this.hideAction();
22499                     if(c){
22500                         c();
22501                     }
22502                 }.createDelegate(this);
22503             }
22504             supr.setVisible.call(this, v, a, d, cb, e);
22505             if(v){
22506                 this.sync(true);
22507             }else if(!a){
22508                 this.hideAction();
22509             }
22510         }
22511     },
22512
22513     storeXY : function(xy){
22514         delete this.lastLT;
22515         this.lastXY = xy;
22516     },
22517
22518     storeLeftTop : function(left, top){
22519         delete this.lastXY;
22520         this.lastLT = [left, top];
22521     },
22522
22523     // private
22524     beforeFx : function(){
22525         this.beforeAction();
22526         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22527     },
22528
22529     // private
22530     afterFx : function(){
22531         Roo.Layer.superclass.afterFx.apply(this, arguments);
22532         this.sync(this.isVisible());
22533     },
22534
22535     // private
22536     beforeAction : function(){
22537         if(!this.updating && this.shadow){
22538             this.shadow.hide();
22539         }
22540     },
22541
22542     // overridden Element method
22543     setLeft : function(left){
22544         this.storeLeftTop(left, this.getTop(true));
22545         supr.setLeft.apply(this, arguments);
22546         this.sync();
22547     },
22548
22549     setTop : function(top){
22550         this.storeLeftTop(this.getLeft(true), top);
22551         supr.setTop.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setLeftTop : function(left, top){
22556         this.storeLeftTop(left, top);
22557         supr.setLeftTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setXY : function(xy, a, d, c, e){
22562         this.fixDisplay();
22563         this.beforeAction();
22564         this.storeXY(xy);
22565         var cb = this.createCB(c);
22566         supr.setXY.call(this, xy, a, d, cb, e);
22567         if(!a){
22568             cb();
22569         }
22570     },
22571
22572     // private
22573     createCB : function(c){
22574         var el = this;
22575         return function(){
22576             el.constrainXY();
22577             el.sync(true);
22578             if(c){
22579                 c();
22580             }
22581         };
22582     },
22583
22584     // overridden Element method
22585     setX : function(x, a, d, c, e){
22586         this.setXY([x, this.getY()], a, d, c, e);
22587     },
22588
22589     // overridden Element method
22590     setY : function(y, a, d, c, e){
22591         this.setXY([this.getX(), y], a, d, c, e);
22592     },
22593
22594     // overridden Element method
22595     setSize : function(w, h, a, d, c, e){
22596         this.beforeAction();
22597         var cb = this.createCB(c);
22598         supr.setSize.call(this, w, h, a, d, cb, e);
22599         if(!a){
22600             cb();
22601         }
22602     },
22603
22604     // overridden Element method
22605     setWidth : function(w, a, d, c, e){
22606         this.beforeAction();
22607         var cb = this.createCB(c);
22608         supr.setWidth.call(this, w, a, d, cb, e);
22609         if(!a){
22610             cb();
22611         }
22612     },
22613
22614     // overridden Element method
22615     setHeight : function(h, a, d, c, e){
22616         this.beforeAction();
22617         var cb = this.createCB(c);
22618         supr.setHeight.call(this, h, a, d, cb, e);
22619         if(!a){
22620             cb();
22621         }
22622     },
22623
22624     // overridden Element method
22625     setBounds : function(x, y, w, h, a, d, c, e){
22626         this.beforeAction();
22627         var cb = this.createCB(c);
22628         if(!a){
22629             this.storeXY([x, y]);
22630             supr.setXY.call(this, [x, y]);
22631             supr.setSize.call(this, w, h, a, d, cb, e);
22632             cb();
22633         }else{
22634             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22635         }
22636         return this;
22637     },
22638     
22639     /**
22640      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22641      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22642      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22643      * @param {Number} zindex The new z-index to set
22644      * @return {this} The Layer
22645      */
22646     setZIndex : function(zindex){
22647         this.zindex = zindex;
22648         this.setStyle("z-index", zindex + 2);
22649         if(this.shadow){
22650             this.shadow.setZIndex(zindex + 1);
22651         }
22652         if(this.shim){
22653             this.shim.setStyle("z-index", zindex);
22654         }
22655     }
22656 });
22657 })();/*
22658  * Based on:
22659  * Ext JS Library 1.1.1
22660  * Copyright(c) 2006-2007, Ext JS, LLC.
22661  *
22662  * Originally Released Under LGPL - original licence link has changed is not relivant.
22663  *
22664  * Fork - LGPL
22665  * <script type="text/javascript">
22666  */
22667
22668
22669 /**
22670  * @class Roo.Shadow
22671  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22672  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22673  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22674  * @constructor
22675  * Create a new Shadow
22676  * @param {Object} config The config object
22677  */
22678 Roo.Shadow = function(config){
22679     Roo.apply(this, config);
22680     if(typeof this.mode != "string"){
22681         this.mode = this.defaultMode;
22682     }
22683     var o = this.offset, a = {h: 0};
22684     var rad = Math.floor(this.offset/2);
22685     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22686         case "drop":
22687             a.w = 0;
22688             a.l = a.t = o;
22689             a.t -= 1;
22690             if(Roo.isIE){
22691                 a.l -= this.offset + rad;
22692                 a.t -= this.offset + rad;
22693                 a.w -= rad;
22694                 a.h -= rad;
22695                 a.t += 1;
22696             }
22697         break;
22698         case "sides":
22699             a.w = (o*2);
22700             a.l = -o;
22701             a.t = o-1;
22702             if(Roo.isIE){
22703                 a.l -= (this.offset - rad);
22704                 a.t -= this.offset + rad;
22705                 a.l += 1;
22706                 a.w -= (this.offset - rad)*2;
22707                 a.w -= rad + 1;
22708                 a.h -= 1;
22709             }
22710         break;
22711         case "frame":
22712             a.w = a.h = (o*2);
22713             a.l = a.t = -o;
22714             a.t += 1;
22715             a.h -= 2;
22716             if(Roo.isIE){
22717                 a.l -= (this.offset - rad);
22718                 a.t -= (this.offset - rad);
22719                 a.l += 1;
22720                 a.w -= (this.offset + rad + 1);
22721                 a.h -= (this.offset + rad);
22722                 a.h += 1;
22723             }
22724         break;
22725     };
22726
22727     this.adjusts = a;
22728 };
22729
22730 Roo.Shadow.prototype = {
22731     /**
22732      * @cfg {String} mode
22733      * The shadow display mode.  Supports the following options:<br />
22734      * sides: Shadow displays on both sides and bottom only<br />
22735      * frame: Shadow displays equally on all four sides<br />
22736      * drop: Traditional bottom-right drop shadow (default)
22737      */
22738     /**
22739      * @cfg {String} offset
22740      * The number of pixels to offset the shadow from the element (defaults to 4)
22741      */
22742     offset: 4,
22743
22744     // private
22745     defaultMode: "drop",
22746
22747     /**
22748      * Displays the shadow under the target element
22749      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22750      */
22751     show : function(target){
22752         target = Roo.get(target);
22753         if(!this.el){
22754             this.el = Roo.Shadow.Pool.pull();
22755             if(this.el.dom.nextSibling != target.dom){
22756                 this.el.insertBefore(target);
22757             }
22758         }
22759         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22760         if(Roo.isIE){
22761             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22762         }
22763         this.realign(
22764             target.getLeft(true),
22765             target.getTop(true),
22766             target.getWidth(),
22767             target.getHeight()
22768         );
22769         this.el.dom.style.display = "block";
22770     },
22771
22772     /**
22773      * Returns true if the shadow is visible, else false
22774      */
22775     isVisible : function(){
22776         return this.el ? true : false;  
22777     },
22778
22779     /**
22780      * Direct alignment when values are already available. Show must be called at least once before
22781      * calling this method to ensure it is initialized.
22782      * @param {Number} left The target element left position
22783      * @param {Number} top The target element top position
22784      * @param {Number} width The target element width
22785      * @param {Number} height The target element height
22786      */
22787     realign : function(l, t, w, h){
22788         if(!this.el){
22789             return;
22790         }
22791         var a = this.adjusts, d = this.el.dom, s = d.style;
22792         var iea = 0;
22793         s.left = (l+a.l)+"px";
22794         s.top = (t+a.t)+"px";
22795         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22796  
22797         if(s.width != sws || s.height != shs){
22798             s.width = sws;
22799             s.height = shs;
22800             if(!Roo.isIE){
22801                 var cn = d.childNodes;
22802                 var sww = Math.max(0, (sw-12))+"px";
22803                 cn[0].childNodes[1].style.width = sww;
22804                 cn[1].childNodes[1].style.width = sww;
22805                 cn[2].childNodes[1].style.width = sww;
22806                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22807             }
22808         }
22809     },
22810
22811     /**
22812      * Hides this shadow
22813      */
22814     hide : function(){
22815         if(this.el){
22816             this.el.dom.style.display = "none";
22817             Roo.Shadow.Pool.push(this.el);
22818             delete this.el;
22819         }
22820     },
22821
22822     /**
22823      * Adjust the z-index of this shadow
22824      * @param {Number} zindex The new z-index
22825      */
22826     setZIndex : function(z){
22827         this.zIndex = z;
22828         if(this.el){
22829             this.el.setStyle("z-index", z);
22830         }
22831     }
22832 };
22833
22834 // Private utility class that manages the internal Shadow cache
22835 Roo.Shadow.Pool = function(){
22836     var p = [];
22837     var markup = Roo.isIE ?
22838                  '<div class="x-ie-shadow"></div>' :
22839                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22840     return {
22841         pull : function(){
22842             var sh = p.shift();
22843             if(!sh){
22844                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22845                 sh.autoBoxAdjust = false;
22846             }
22847             return sh;
22848         },
22849
22850         push : function(sh){
22851             p.push(sh);
22852         }
22853     };
22854 }();/*
22855  * Based on:
22856  * Ext JS Library 1.1.1
22857  * Copyright(c) 2006-2007, Ext JS, LLC.
22858  *
22859  * Originally Released Under LGPL - original licence link has changed is not relivant.
22860  *
22861  * Fork - LGPL
22862  * <script type="text/javascript">
22863  */
22864
22865 /**
22866  * @class Roo.BoxComponent
22867  * @extends Roo.Component
22868  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22869  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22870  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22871  * layout containers.
22872  * @constructor
22873  * @param {Roo.Element/String/Object} config The configuration options.
22874  */
22875 Roo.BoxComponent = function(config){
22876     Roo.Component.call(this, config);
22877     this.addEvents({
22878         /**
22879          * @event resize
22880          * Fires after the component is resized.
22881              * @param {Roo.Component} this
22882              * @param {Number} adjWidth The box-adjusted width that was set
22883              * @param {Number} adjHeight The box-adjusted height that was set
22884              * @param {Number} rawWidth The width that was originally specified
22885              * @param {Number} rawHeight The height that was originally specified
22886              */
22887         resize : true,
22888         /**
22889          * @event move
22890          * Fires after the component is moved.
22891              * @param {Roo.Component} this
22892              * @param {Number} x The new x position
22893              * @param {Number} y The new y position
22894              */
22895         move : true
22896     });
22897 };
22898
22899 Roo.extend(Roo.BoxComponent, Roo.Component, {
22900     // private, set in afterRender to signify that the component has been rendered
22901     boxReady : false,
22902     // private, used to defer height settings to subclasses
22903     deferHeight: false,
22904     /** @cfg {Number} width
22905      * width (optional) size of component
22906      */
22907      /** @cfg {Number} height
22908      * height (optional) size of component
22909      */
22910      
22911     /**
22912      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22913      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22914      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22915      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22916      * @return {Roo.BoxComponent} this
22917      */
22918     setSize : function(w, h){
22919         // support for standard size objects
22920         if(typeof w == 'object'){
22921             h = w.height;
22922             w = w.width;
22923         }
22924         // not rendered
22925         if(!this.boxReady){
22926             this.width = w;
22927             this.height = h;
22928             return this;
22929         }
22930
22931         // prevent recalcs when not needed
22932         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22933             return this;
22934         }
22935         this.lastSize = {width: w, height: h};
22936
22937         var adj = this.adjustSize(w, h);
22938         var aw = adj.width, ah = adj.height;
22939         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22940             var rz = this.getResizeEl();
22941             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22942                 rz.setSize(aw, ah);
22943             }else if(!this.deferHeight && ah !== undefined){
22944                 rz.setHeight(ah);
22945             }else if(aw !== undefined){
22946                 rz.setWidth(aw);
22947             }
22948             this.onResize(aw, ah, w, h);
22949             this.fireEvent('resize', this, aw, ah, w, h);
22950         }
22951         return this;
22952     },
22953
22954     /**
22955      * Gets the current size of the component's underlying element.
22956      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22957      */
22958     getSize : function(){
22959         return this.el.getSize();
22960     },
22961
22962     /**
22963      * Gets the current XY position of the component's underlying element.
22964      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22965      * @return {Array} The XY position of the element (e.g., [100, 200])
22966      */
22967     getPosition : function(local){
22968         if(local === true){
22969             return [this.el.getLeft(true), this.el.getTop(true)];
22970         }
22971         return this.xy || this.el.getXY();
22972     },
22973
22974     /**
22975      * Gets the current box measurements of the component's underlying element.
22976      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22977      * @returns {Object} box An object in the format {x, y, width, height}
22978      */
22979     getBox : function(local){
22980         var s = this.el.getSize();
22981         if(local){
22982             s.x = this.el.getLeft(true);
22983             s.y = this.el.getTop(true);
22984         }else{
22985             var xy = this.xy || this.el.getXY();
22986             s.x = xy[0];
22987             s.y = xy[1];
22988         }
22989         return s;
22990     },
22991
22992     /**
22993      * Sets the current box measurements of the component's underlying element.
22994      * @param {Object} box An object in the format {x, y, width, height}
22995      * @returns {Roo.BoxComponent} this
22996      */
22997     updateBox : function(box){
22998         this.setSize(box.width, box.height);
22999         this.setPagePosition(box.x, box.y);
23000         return this;
23001     },
23002
23003     // protected
23004     getResizeEl : function(){
23005         return this.resizeEl || this.el;
23006     },
23007
23008     // protected
23009     getPositionEl : function(){
23010         return this.positionEl || this.el;
23011     },
23012
23013     /**
23014      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23015      * This method fires the move event.
23016      * @param {Number} left The new left
23017      * @param {Number} top The new top
23018      * @returns {Roo.BoxComponent} this
23019      */
23020     setPosition : function(x, y){
23021         this.x = x;
23022         this.y = y;
23023         if(!this.boxReady){
23024             return this;
23025         }
23026         var adj = this.adjustPosition(x, y);
23027         var ax = adj.x, ay = adj.y;
23028
23029         var el = this.getPositionEl();
23030         if(ax !== undefined || ay !== undefined){
23031             if(ax !== undefined && ay !== undefined){
23032                 el.setLeftTop(ax, ay);
23033             }else if(ax !== undefined){
23034                 el.setLeft(ax);
23035             }else if(ay !== undefined){
23036                 el.setTop(ay);
23037             }
23038             this.onPosition(ax, ay);
23039             this.fireEvent('move', this, ax, ay);
23040         }
23041         return this;
23042     },
23043
23044     /**
23045      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23046      * This method fires the move event.
23047      * @param {Number} x The new x position
23048      * @param {Number} y The new y position
23049      * @returns {Roo.BoxComponent} this
23050      */
23051     setPagePosition : function(x, y){
23052         this.pageX = x;
23053         this.pageY = y;
23054         if(!this.boxReady){
23055             return;
23056         }
23057         if(x === undefined || y === undefined){ // cannot translate undefined points
23058             return;
23059         }
23060         var p = this.el.translatePoints(x, y);
23061         this.setPosition(p.left, p.top);
23062         return this;
23063     },
23064
23065     // private
23066     onRender : function(ct, position){
23067         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23068         if(this.resizeEl){
23069             this.resizeEl = Roo.get(this.resizeEl);
23070         }
23071         if(this.positionEl){
23072             this.positionEl = Roo.get(this.positionEl);
23073         }
23074     },
23075
23076     // private
23077     afterRender : function(){
23078         Roo.BoxComponent.superclass.afterRender.call(this);
23079         this.boxReady = true;
23080         this.setSize(this.width, this.height);
23081         if(this.x || this.y){
23082             this.setPosition(this.x, this.y);
23083         }
23084         if(this.pageX || this.pageY){
23085             this.setPagePosition(this.pageX, this.pageY);
23086         }
23087     },
23088
23089     /**
23090      * Force the component's size to recalculate based on the underlying element's current height and width.
23091      * @returns {Roo.BoxComponent} this
23092      */
23093     syncSize : function(){
23094         delete this.lastSize;
23095         this.setSize(this.el.getWidth(), this.el.getHeight());
23096         return this;
23097     },
23098
23099     /**
23100      * Called after the component is resized, this method is empty by default but can be implemented by any
23101      * subclass that needs to perform custom logic after a resize occurs.
23102      * @param {Number} adjWidth The box-adjusted width that was set
23103      * @param {Number} adjHeight The box-adjusted height that was set
23104      * @param {Number} rawWidth The width that was originally specified
23105      * @param {Number} rawHeight The height that was originally specified
23106      */
23107     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23108
23109     },
23110
23111     /**
23112      * Called after the component is moved, this method is empty by default but can be implemented by any
23113      * subclass that needs to perform custom logic after a move occurs.
23114      * @param {Number} x The new x position
23115      * @param {Number} y The new y position
23116      */
23117     onPosition : function(x, y){
23118
23119     },
23120
23121     // private
23122     adjustSize : function(w, h){
23123         if(this.autoWidth){
23124             w = 'auto';
23125         }
23126         if(this.autoHeight){
23127             h = 'auto';
23128         }
23129         return {width : w, height: h};
23130     },
23131
23132     // private
23133     adjustPosition : function(x, y){
23134         return {x : x, y: y};
23135     }
23136 });/*
23137  * Based on:
23138  * Ext JS Library 1.1.1
23139  * Copyright(c) 2006-2007, Ext JS, LLC.
23140  *
23141  * Originally Released Under LGPL - original licence link has changed is not relivant.
23142  *
23143  * Fork - LGPL
23144  * <script type="text/javascript">
23145  */
23146
23147
23148 /**
23149  * @class Roo.SplitBar
23150  * @extends Roo.util.Observable
23151  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23152  * <br><br>
23153  * Usage:
23154  * <pre><code>
23155 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23156                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23157 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23158 split.minSize = 100;
23159 split.maxSize = 600;
23160 split.animate = true;
23161 split.on('moved', splitterMoved);
23162 </code></pre>
23163  * @constructor
23164  * Create a new SplitBar
23165  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23166  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23167  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23168  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23169                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23170                         position of the SplitBar).
23171  */
23172 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23173     
23174     /** @private */
23175     this.el = Roo.get(dragElement, true);
23176     this.el.dom.unselectable = "on";
23177     /** @private */
23178     this.resizingEl = Roo.get(resizingElement, true);
23179
23180     /**
23181      * @private
23182      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23183      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23184      * @type Number
23185      */
23186     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23187     
23188     /**
23189      * The minimum size of the resizing element. (Defaults to 0)
23190      * @type Number
23191      */
23192     this.minSize = 0;
23193     
23194     /**
23195      * The maximum size of the resizing element. (Defaults to 2000)
23196      * @type Number
23197      */
23198     this.maxSize = 2000;
23199     
23200     /**
23201      * Whether to animate the transition to the new size
23202      * @type Boolean
23203      */
23204     this.animate = false;
23205     
23206     /**
23207      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23208      * @type Boolean
23209      */
23210     this.useShim = false;
23211     
23212     /** @private */
23213     this.shim = null;
23214     
23215     if(!existingProxy){
23216         /** @private */
23217         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23218     }else{
23219         this.proxy = Roo.get(existingProxy).dom;
23220     }
23221     /** @private */
23222     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23223     
23224     /** @private */
23225     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23226     
23227     /** @private */
23228     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23229     
23230     /** @private */
23231     this.dragSpecs = {};
23232     
23233     /**
23234      * @private The adapter to use to positon and resize elements
23235      */
23236     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23237     this.adapter.init(this);
23238     
23239     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23240         /** @private */
23241         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23242         this.el.addClass("x-splitbar-h");
23243     }else{
23244         /** @private */
23245         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23246         this.el.addClass("x-splitbar-v");
23247     }
23248     
23249     this.addEvents({
23250         /**
23251          * @event resize
23252          * Fires when the splitter is moved (alias for {@link #event-moved})
23253          * @param {Roo.SplitBar} this
23254          * @param {Number} newSize the new width or height
23255          */
23256         "resize" : true,
23257         /**
23258          * @event moved
23259          * Fires when the splitter is moved
23260          * @param {Roo.SplitBar} this
23261          * @param {Number} newSize the new width or height
23262          */
23263         "moved" : true,
23264         /**
23265          * @event beforeresize
23266          * Fires before the splitter is dragged
23267          * @param {Roo.SplitBar} this
23268          */
23269         "beforeresize" : true,
23270
23271         "beforeapply" : true
23272     });
23273
23274     Roo.util.Observable.call(this);
23275 };
23276
23277 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23278     onStartProxyDrag : function(x, y){
23279         this.fireEvent("beforeresize", this);
23280         if(!this.overlay){
23281             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23282             o.unselectable();
23283             o.enableDisplayMode("block");
23284             // all splitbars share the same overlay
23285             Roo.SplitBar.prototype.overlay = o;
23286         }
23287         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23288         this.overlay.show();
23289         Roo.get(this.proxy).setDisplayed("block");
23290         var size = this.adapter.getElementSize(this);
23291         this.activeMinSize = this.getMinimumSize();;
23292         this.activeMaxSize = this.getMaximumSize();;
23293         var c1 = size - this.activeMinSize;
23294         var c2 = Math.max(this.activeMaxSize - size, 0);
23295         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23296             this.dd.resetConstraints();
23297             this.dd.setXConstraint(
23298                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23299                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23300             );
23301             this.dd.setYConstraint(0, 0);
23302         }else{
23303             this.dd.resetConstraints();
23304             this.dd.setXConstraint(0, 0);
23305             this.dd.setYConstraint(
23306                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23307                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23308             );
23309          }
23310         this.dragSpecs.startSize = size;
23311         this.dragSpecs.startPoint = [x, y];
23312         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23313     },
23314     
23315     /** 
23316      * @private Called after the drag operation by the DDProxy
23317      */
23318     onEndProxyDrag : function(e){
23319         Roo.get(this.proxy).setDisplayed(false);
23320         var endPoint = Roo.lib.Event.getXY(e);
23321         if(this.overlay){
23322             this.overlay.hide();
23323         }
23324         var newSize;
23325         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23326             newSize = this.dragSpecs.startSize + 
23327                 (this.placement == Roo.SplitBar.LEFT ?
23328                     endPoint[0] - this.dragSpecs.startPoint[0] :
23329                     this.dragSpecs.startPoint[0] - endPoint[0]
23330                 );
23331         }else{
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.TOP ?
23334                     endPoint[1] - this.dragSpecs.startPoint[1] :
23335                     this.dragSpecs.startPoint[1] - endPoint[1]
23336                 );
23337         }
23338         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23339         if(newSize != this.dragSpecs.startSize){
23340             if(this.fireEvent('beforeapply', this, newSize) !== false){
23341                 this.adapter.setElementSize(this, newSize);
23342                 this.fireEvent("moved", this, newSize);
23343                 this.fireEvent("resize", this, newSize);
23344             }
23345         }
23346     },
23347     
23348     /**
23349      * Get the adapter this SplitBar uses
23350      * @return The adapter object
23351      */
23352     getAdapter : function(){
23353         return this.adapter;
23354     },
23355     
23356     /**
23357      * Set the adapter this SplitBar uses
23358      * @param {Object} adapter A SplitBar adapter object
23359      */
23360     setAdapter : function(adapter){
23361         this.adapter = adapter;
23362         this.adapter.init(this);
23363     },
23364     
23365     /**
23366      * Gets the minimum size for the resizing element
23367      * @return {Number} The minimum size
23368      */
23369     getMinimumSize : function(){
23370         return this.minSize;
23371     },
23372     
23373     /**
23374      * Sets the minimum size for the resizing element
23375      * @param {Number} minSize The minimum size
23376      */
23377     setMinimumSize : function(minSize){
23378         this.minSize = minSize;
23379     },
23380     
23381     /**
23382      * Gets the maximum size for the resizing element
23383      * @return {Number} The maximum size
23384      */
23385     getMaximumSize : function(){
23386         return this.maxSize;
23387     },
23388     
23389     /**
23390      * Sets the maximum size for the resizing element
23391      * @param {Number} maxSize The maximum size
23392      */
23393     setMaximumSize : function(maxSize){
23394         this.maxSize = maxSize;
23395     },
23396     
23397     /**
23398      * Sets the initialize size for the resizing element
23399      * @param {Number} size The initial size
23400      */
23401     setCurrentSize : function(size){
23402         var oldAnimate = this.animate;
23403         this.animate = false;
23404         this.adapter.setElementSize(this, size);
23405         this.animate = oldAnimate;
23406     },
23407     
23408     /**
23409      * Destroy this splitbar. 
23410      * @param {Boolean} removeEl True to remove the element
23411      */
23412     destroy : function(removeEl){
23413         if(this.shim){
23414             this.shim.remove();
23415         }
23416         this.dd.unreg();
23417         this.proxy.parentNode.removeChild(this.proxy);
23418         if(removeEl){
23419             this.el.remove();
23420         }
23421     }
23422 });
23423
23424 /**
23425  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23426  */
23427 Roo.SplitBar.createProxy = function(dir){
23428     var proxy = new Roo.Element(document.createElement("div"));
23429     proxy.unselectable();
23430     var cls = 'x-splitbar-proxy';
23431     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23432     document.body.appendChild(proxy.dom);
23433     return proxy.dom;
23434 };
23435
23436 /** 
23437  * @class Roo.SplitBar.BasicLayoutAdapter
23438  * Default Adapter. It assumes the splitter and resizing element are not positioned
23439  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23440  */
23441 Roo.SplitBar.BasicLayoutAdapter = function(){
23442 };
23443
23444 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23445     // do nothing for now
23446     init : function(s){
23447     
23448     },
23449     /**
23450      * Called before drag operations to get the current size of the resizing element. 
23451      * @param {Roo.SplitBar} s The SplitBar using this adapter
23452      */
23453      getElementSize : function(s){
23454         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23455             return s.resizingEl.getWidth();
23456         }else{
23457             return s.resizingEl.getHeight();
23458         }
23459     },
23460     
23461     /**
23462      * Called after drag operations to set the size of the resizing element.
23463      * @param {Roo.SplitBar} s The SplitBar using this adapter
23464      * @param {Number} newSize The new size to set
23465      * @param {Function} onComplete A function to be invoked when resizing is complete
23466      */
23467     setElementSize : function(s, newSize, onComplete){
23468         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23469             if(!s.animate){
23470                 s.resizingEl.setWidth(newSize);
23471                 if(onComplete){
23472                     onComplete(s, newSize);
23473                 }
23474             }else{
23475                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23476             }
23477         }else{
23478             
23479             if(!s.animate){
23480                 s.resizingEl.setHeight(newSize);
23481                 if(onComplete){
23482                     onComplete(s, newSize);
23483                 }
23484             }else{
23485                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23486             }
23487         }
23488     }
23489 };
23490
23491 /** 
23492  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23493  * @extends Roo.SplitBar.BasicLayoutAdapter
23494  * Adapter that  moves the splitter element to align with the resized sizing element. 
23495  * Used with an absolute positioned SplitBar.
23496  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23497  * document.body, make sure you assign an id to the body element.
23498  */
23499 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23500     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23501     this.container = Roo.get(container);
23502 };
23503
23504 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23505     init : function(s){
23506         this.basic.init(s);
23507     },
23508     
23509     getElementSize : function(s){
23510         return this.basic.getElementSize(s);
23511     },
23512     
23513     setElementSize : function(s, newSize, onComplete){
23514         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23515     },
23516     
23517     moveSplitter : function(s){
23518         var yes = Roo.SplitBar;
23519         switch(s.placement){
23520             case yes.LEFT:
23521                 s.el.setX(s.resizingEl.getRight());
23522                 break;
23523             case yes.RIGHT:
23524                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23525                 break;
23526             case yes.TOP:
23527                 s.el.setY(s.resizingEl.getBottom());
23528                 break;
23529             case yes.BOTTOM:
23530                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23531                 break;
23532         }
23533     }
23534 };
23535
23536 /**
23537  * Orientation constant - Create a vertical SplitBar
23538  * @static
23539  * @type Number
23540  */
23541 Roo.SplitBar.VERTICAL = 1;
23542
23543 /**
23544  * Orientation constant - Create a horizontal SplitBar
23545  * @static
23546  * @type Number
23547  */
23548 Roo.SplitBar.HORIZONTAL = 2;
23549
23550 /**
23551  * Placement constant - The resizing element is to the left of the splitter element
23552  * @static
23553  * @type Number
23554  */
23555 Roo.SplitBar.LEFT = 1;
23556
23557 /**
23558  * Placement constant - The resizing element is to the right of the splitter element
23559  * @static
23560  * @type Number
23561  */
23562 Roo.SplitBar.RIGHT = 2;
23563
23564 /**
23565  * Placement constant - The resizing element is positioned above the splitter element
23566  * @static
23567  * @type Number
23568  */
23569 Roo.SplitBar.TOP = 3;
23570
23571 /**
23572  * Placement constant - The resizing element is positioned under splitter element
23573  * @static
23574  * @type Number
23575  */
23576 Roo.SplitBar.BOTTOM = 4;
23577 /*
23578  * Based on:
23579  * Ext JS Library 1.1.1
23580  * Copyright(c) 2006-2007, Ext JS, LLC.
23581  *
23582  * Originally Released Under LGPL - original licence link has changed is not relivant.
23583  *
23584  * Fork - LGPL
23585  * <script type="text/javascript">
23586  */
23587
23588 /**
23589  * @class Roo.View
23590  * @extends Roo.util.Observable
23591  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23592  * This class also supports single and multi selection modes. <br>
23593  * Create a data model bound view:
23594  <pre><code>
23595  var store = new Roo.data.Store(...);
23596
23597  var view = new Roo.View({
23598     el : "my-element",
23599     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23600  
23601     singleSelect: true,
23602     selectedClass: "ydataview-selected",
23603     store: store
23604  });
23605
23606  // listen for node click?
23607  view.on("click", function(vw, index, node, e){
23608  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23609  });
23610
23611  // load XML data
23612  dataModel.load("foobar.xml");
23613  </code></pre>
23614  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23615  * <br><br>
23616  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23617  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23618  * 
23619  * Note: old style constructor is still suported (container, template, config)
23620  * 
23621  * @constructor
23622  * Create a new View
23623  * @param {Object} config The config object
23624  * 
23625  */
23626 Roo.View = function(config, depreciated_tpl, depreciated_config){
23627     
23628     if (typeof(depreciated_tpl) == 'undefined') {
23629         // new way.. - universal constructor.
23630         Roo.apply(this, config);
23631         this.el  = Roo.get(this.el);
23632     } else {
23633         // old format..
23634         this.el  = Roo.get(config);
23635         this.tpl = depreciated_tpl;
23636         Roo.apply(this, depreciated_config);
23637     }
23638      
23639     
23640     if(typeof(this.tpl) == "string"){
23641         this.tpl = new Roo.Template(this.tpl);
23642     } else {
23643         // support xtype ctors..
23644         this.tpl = new Roo.factory(this.tpl, Roo);
23645     }
23646     
23647     
23648     this.tpl.compile();
23649    
23650
23651      
23652     /** @private */
23653     this.addEvents({
23654         /**
23655          * @event beforeclick
23656          * Fires before a click is processed. Returns false to cancel the default action.
23657          * @param {Roo.View} this
23658          * @param {Number} index The index of the target node
23659          * @param {HTMLElement} node The target node
23660          * @param {Roo.EventObject} e The raw event object
23661          */
23662             "beforeclick" : true,
23663         /**
23664          * @event click
23665          * Fires when a template node is clicked.
23666          * @param {Roo.View} this
23667          * @param {Number} index The index of the target node
23668          * @param {HTMLElement} node The target node
23669          * @param {Roo.EventObject} e The raw event object
23670          */
23671             "click" : true,
23672         /**
23673          * @event dblclick
23674          * Fires when a template node is double clicked.
23675          * @param {Roo.View} this
23676          * @param {Number} index The index of the target node
23677          * @param {HTMLElement} node The target node
23678          * @param {Roo.EventObject} e The raw event object
23679          */
23680             "dblclick" : true,
23681         /**
23682          * @event contextmenu
23683          * Fires when a template node is right clicked.
23684          * @param {Roo.View} this
23685          * @param {Number} index The index of the target node
23686          * @param {HTMLElement} node The target node
23687          * @param {Roo.EventObject} e The raw event object
23688          */
23689             "contextmenu" : true,
23690         /**
23691          * @event selectionchange
23692          * Fires when the selected nodes change.
23693          * @param {Roo.View} this
23694          * @param {Array} selections Array of the selected nodes
23695          */
23696             "selectionchange" : true,
23697     
23698         /**
23699          * @event beforeselect
23700          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23701          * @param {Roo.View} this
23702          * @param {HTMLElement} node The node to be selected
23703          * @param {Array} selections Array of currently selected nodes
23704          */
23705             "beforeselect" : true,
23706         /**
23707          * @event preparedata
23708          * Fires on every row to render, to allow you to change the data.
23709          * @param {Roo.View} this
23710          * @param {Object} data to be rendered (change this)
23711          */
23712           "preparedata" : true
23713         });
23714
23715     this.el.on({
23716         "click": this.onClick,
23717         "dblclick": this.onDblClick,
23718         "contextmenu": this.onContextMenu,
23719         scope:this
23720     });
23721
23722     this.selections = [];
23723     this.nodes = [];
23724     this.cmp = new Roo.CompositeElementLite([]);
23725     if(this.store){
23726         this.store = Roo.factory(this.store, Roo.data);
23727         this.setStore(this.store, true);
23728     }
23729     Roo.View.superclass.constructor.call(this);
23730 };
23731
23732 Roo.extend(Roo.View, Roo.util.Observable, {
23733     
23734      /**
23735      * @cfg {Roo.data.Store} store Data store to load data from.
23736      */
23737     store : false,
23738     
23739     /**
23740      * @cfg {String|Roo.Element} el The container element.
23741      */
23742     el : '',
23743     
23744     /**
23745      * @cfg {String|Roo.Template} tpl The template used by this View 
23746      */
23747     tpl : false,
23748     
23749     /**
23750      * @cfg {String} selectedClass The css class to add to selected nodes
23751      */
23752     selectedClass : "x-view-selected",
23753      /**
23754      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23755      */
23756     emptyText : "",
23757     /**
23758      * @cfg {Boolean} multiSelect Allow multiple selection
23759      */
23760     multiSelect : false,
23761     /**
23762      * @cfg {Boolean} singleSelect Allow single selection
23763      */
23764     singleSelect:  false,
23765     
23766     /**
23767      * @cfg {Boolean} toggleSelect - selecting 
23768      */
23769     toggleSelect : false,
23770     
23771     /**
23772      * Returns the element this view is bound to.
23773      * @return {Roo.Element}
23774      */
23775     getEl : function(){
23776         return this.el;
23777     },
23778
23779     /**
23780      * Refreshes the view.
23781      */
23782     refresh : function(){
23783         var t = this.tpl;
23784         this.clearSelections();
23785         this.el.update("");
23786         var html = [];
23787         var records = this.store.getRange();
23788         if(records.length < 1){
23789             this.el.update(this.emptyText);
23790             return;
23791         }
23792         for(var i = 0, len = records.length; i < len; i++){
23793             var data = this.prepareData(records[i].data, i, records[i]);
23794             this.fireEvent("preparedata", this, data, i, records[i]);
23795             html[html.length] = t.apply(data);
23796         }
23797         this.el.update(html.join(""));
23798         this.nodes = this.el.dom.childNodes;
23799         this.updateIndexes(0);
23800     },
23801
23802     /**
23803      * Function to override to reformat the data that is sent to
23804      * the template for each node.
23805      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23806      * a JSON object for an UpdateManager bound view).
23807      */
23808     prepareData : function(data){
23809         return data;
23810     },
23811
23812     onUpdate : function(ds, record){
23813         this.clearSelections();
23814         var index = this.store.indexOf(record);
23815         var n = this.nodes[index];
23816         this.tpl.insertBefore(n, this.prepareData(record.data));
23817         n.parentNode.removeChild(n);
23818         this.updateIndexes(index, index);
23819     },
23820
23821     onAdd : function(ds, records, index){
23822         this.clearSelections();
23823         if(this.nodes.length == 0){
23824             this.refresh();
23825             return;
23826         }
23827         var n = this.nodes[index];
23828         for(var i = 0, len = records.length; i < len; i++){
23829             var d = this.prepareData(records[i].data);
23830             if(n){
23831                 this.tpl.insertBefore(n, d);
23832             }else{
23833                 this.tpl.append(this.el, d);
23834             }
23835         }
23836         this.updateIndexes(index);
23837     },
23838
23839     onRemove : function(ds, record, index){
23840         this.clearSelections();
23841         this.el.dom.removeChild(this.nodes[index]);
23842         this.updateIndexes(index);
23843     },
23844
23845     /**
23846      * Refresh an individual node.
23847      * @param {Number} index
23848      */
23849     refreshNode : function(index){
23850         this.onUpdate(this.store, this.store.getAt(index));
23851     },
23852
23853     updateIndexes : function(startIndex, endIndex){
23854         var ns = this.nodes;
23855         startIndex = startIndex || 0;
23856         endIndex = endIndex || ns.length - 1;
23857         for(var i = startIndex; i <= endIndex; i++){
23858             ns[i].nodeIndex = i;
23859         }
23860     },
23861
23862     /**
23863      * Changes the data store this view uses and refresh the view.
23864      * @param {Store} store
23865      */
23866     setStore : function(store, initial){
23867         if(!initial && this.store){
23868             this.store.un("datachanged", this.refresh);
23869             this.store.un("add", this.onAdd);
23870             this.store.un("remove", this.onRemove);
23871             this.store.un("update", this.onUpdate);
23872             this.store.un("clear", this.refresh);
23873         }
23874         if(store){
23875           
23876             store.on("datachanged", this.refresh, this);
23877             store.on("add", this.onAdd, this);
23878             store.on("remove", this.onRemove, this);
23879             store.on("update", this.onUpdate, this);
23880             store.on("clear", this.refresh, this);
23881         }
23882         
23883         if(store){
23884             this.refresh();
23885         }
23886     },
23887
23888     /**
23889      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23890      * @param {HTMLElement} node
23891      * @return {HTMLElement} The template node
23892      */
23893     findItemFromChild : function(node){
23894         var el = this.el.dom;
23895         if(!node || node.parentNode == el){
23896                     return node;
23897             }
23898             var p = node.parentNode;
23899             while(p && p != el){
23900             if(p.parentNode == el){
23901                 return p;
23902             }
23903             p = p.parentNode;
23904         }
23905             return null;
23906     },
23907
23908     /** @ignore */
23909     onClick : function(e){
23910         var item = this.findItemFromChild(e.getTarget());
23911         if(item){
23912             var index = this.indexOf(item);
23913             if(this.onItemClick(item, index, e) !== false){
23914                 this.fireEvent("click", this, index, item, e);
23915             }
23916         }else{
23917             this.clearSelections();
23918         }
23919     },
23920
23921     /** @ignore */
23922     onContextMenu : function(e){
23923         var item = this.findItemFromChild(e.getTarget());
23924         if(item){
23925             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23926         }
23927     },
23928
23929     /** @ignore */
23930     onDblClick : function(e){
23931         var item = this.findItemFromChild(e.getTarget());
23932         if(item){
23933             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23934         }
23935     },
23936
23937     onItemClick : function(item, index, e)
23938     {
23939         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23940             return false;
23941         }
23942         if (this.toggleSelect) {
23943             var m = this.isSelected(item) ? 'unselect' : 'select';
23944             Roo.log(m);
23945             var _t = this;
23946             _t[m](item, true, false);
23947             return true;
23948         }
23949         if(this.multiSelect || this.singleSelect){
23950             if(this.multiSelect && e.shiftKey && this.lastSelection){
23951                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23952             }else{
23953                 this.select(item, this.multiSelect && e.ctrlKey);
23954                 this.lastSelection = item;
23955             }
23956             e.preventDefault();
23957         }
23958         return true;
23959     },
23960
23961     /**
23962      * Get the number of selected nodes.
23963      * @return {Number}
23964      */
23965     getSelectionCount : function(){
23966         return this.selections.length;
23967     },
23968
23969     /**
23970      * Get the currently selected nodes.
23971      * @return {Array} An array of HTMLElements
23972      */
23973     getSelectedNodes : function(){
23974         return this.selections;
23975     },
23976
23977     /**
23978      * Get the indexes of the selected nodes.
23979      * @return {Array}
23980      */
23981     getSelectedIndexes : function(){
23982         var indexes = [], s = this.selections;
23983         for(var i = 0, len = s.length; i < len; i++){
23984             indexes.push(s[i].nodeIndex);
23985         }
23986         return indexes;
23987     },
23988
23989     /**
23990      * Clear all selections
23991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23992      */
23993     clearSelections : function(suppressEvent){
23994         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23995             this.cmp.elements = this.selections;
23996             this.cmp.removeClass(this.selectedClass);
23997             this.selections = [];
23998             if(!suppressEvent){
23999                 this.fireEvent("selectionchange", this, this.selections);
24000             }
24001         }
24002     },
24003
24004     /**
24005      * Returns true if the passed node is selected
24006      * @param {HTMLElement/Number} node The node or node index
24007      * @return {Boolean}
24008      */
24009     isSelected : function(node){
24010         var s = this.selections;
24011         if(s.length < 1){
24012             return false;
24013         }
24014         node = this.getNode(node);
24015         return s.indexOf(node) !== -1;
24016     },
24017
24018     /**
24019      * Selects nodes.
24020      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24021      * @param {Boolean} keepExisting (optional) true to keep existing selections
24022      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24023      */
24024     select : function(nodeInfo, keepExisting, suppressEvent){
24025         if(nodeInfo instanceof Array){
24026             if(!keepExisting){
24027                 this.clearSelections(true);
24028             }
24029             for(var i = 0, len = nodeInfo.length; i < len; i++){
24030                 this.select(nodeInfo[i], true, true);
24031             }
24032             return;
24033         } 
24034         var node = this.getNode(nodeInfo);
24035         if(!node || this.isSelected(node)){
24036             return; // already selected.
24037         }
24038         if(!keepExisting){
24039             this.clearSelections(true);
24040         }
24041         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24042             Roo.fly(node).addClass(this.selectedClass);
24043             this.selections.push(node);
24044             if(!suppressEvent){
24045                 this.fireEvent("selectionchange", this, this.selections);
24046             }
24047         }
24048         
24049         
24050     },
24051       /**
24052      * Unselects nodes.
24053      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24054      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24055      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24056      */
24057     unselect : function(nodeInfo, keepExisting, suppressEvent)
24058     {
24059         if(nodeInfo instanceof Array){
24060             Roo.each(this.selections, function(s) {
24061                 this.unselect(s, nodeInfo);
24062             }, this);
24063             return;
24064         }
24065         var node = this.getNode(nodeInfo);
24066         if(!node || !this.isSelected(node)){
24067             Roo.log("not selected");
24068             return; // not selected.
24069         }
24070         // fireevent???
24071         var ns = [];
24072         Roo.each(this.selections, function(s) {
24073             if (s == node ) {
24074                 Roo.fly(node).removeClass(this.selectedClass);
24075
24076                 return;
24077             }
24078             ns.push(s);
24079         },this);
24080         
24081         this.selections= ns;
24082         this.fireEvent("selectionchange", this, this.selections);
24083     },
24084
24085     /**
24086      * Gets a template node.
24087      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24088      * @return {HTMLElement} The node or null if it wasn't found
24089      */
24090     getNode : function(nodeInfo){
24091         if(typeof nodeInfo == "string"){
24092             return document.getElementById(nodeInfo);
24093         }else if(typeof nodeInfo == "number"){
24094             return this.nodes[nodeInfo];
24095         }
24096         return nodeInfo;
24097     },
24098
24099     /**
24100      * Gets a range template nodes.
24101      * @param {Number} startIndex
24102      * @param {Number} endIndex
24103      * @return {Array} An array of nodes
24104      */
24105     getNodes : function(start, end){
24106         var ns = this.nodes;
24107         start = start || 0;
24108         end = typeof end == "undefined" ? ns.length - 1 : end;
24109         var nodes = [];
24110         if(start <= end){
24111             for(var i = start; i <= end; i++){
24112                 nodes.push(ns[i]);
24113             }
24114         } else{
24115             for(var i = start; i >= end; i--){
24116                 nodes.push(ns[i]);
24117             }
24118         }
24119         return nodes;
24120     },
24121
24122     /**
24123      * Finds the index of the passed node
24124      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24125      * @return {Number} The index of the node or -1
24126      */
24127     indexOf : function(node){
24128         node = this.getNode(node);
24129         if(typeof node.nodeIndex == "number"){
24130             return node.nodeIndex;
24131         }
24132         var ns = this.nodes;
24133         for(var i = 0, len = ns.length; i < len; i++){
24134             if(ns[i] == node){
24135                 return i;
24136             }
24137         }
24138         return -1;
24139     }
24140 });
24141 /*
24142  * Based on:
24143  * Ext JS Library 1.1.1
24144  * Copyright(c) 2006-2007, Ext JS, LLC.
24145  *
24146  * Originally Released Under LGPL - original licence link has changed is not relivant.
24147  *
24148  * Fork - LGPL
24149  * <script type="text/javascript">
24150  */
24151
24152 /**
24153  * @class Roo.JsonView
24154  * @extends Roo.View
24155  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24156 <pre><code>
24157 var view = new Roo.JsonView({
24158     container: "my-element",
24159     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24160     multiSelect: true, 
24161     jsonRoot: "data" 
24162 });
24163
24164 // listen for node click?
24165 view.on("click", function(vw, index, node, e){
24166     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24167 });
24168
24169 // direct load of JSON data
24170 view.load("foobar.php");
24171
24172 // Example from my blog list
24173 var tpl = new Roo.Template(
24174     '&lt;div class="entry"&gt;' +
24175     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24176     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24177     "&lt;/div&gt;&lt;hr /&gt;"
24178 );
24179
24180 var moreView = new Roo.JsonView({
24181     container :  "entry-list", 
24182     template : tpl,
24183     jsonRoot: "posts"
24184 });
24185 moreView.on("beforerender", this.sortEntries, this);
24186 moreView.load({
24187     url: "/blog/get-posts.php",
24188     params: "allposts=true",
24189     text: "Loading Blog Entries..."
24190 });
24191 </code></pre>
24192
24193 * Note: old code is supported with arguments : (container, template, config)
24194
24195
24196  * @constructor
24197  * Create a new JsonView
24198  * 
24199  * @param {Object} config The config object
24200  * 
24201  */
24202 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24203     
24204     
24205     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24206
24207     var um = this.el.getUpdateManager();
24208     um.setRenderer(this);
24209     um.on("update", this.onLoad, this);
24210     um.on("failure", this.onLoadException, this);
24211
24212     /**
24213      * @event beforerender
24214      * Fires before rendering of the downloaded JSON data.
24215      * @param {Roo.JsonView} this
24216      * @param {Object} data The JSON data loaded
24217      */
24218     /**
24219      * @event load
24220      * Fires when data is loaded.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      * @param {Object} response The raw Connect response object
24224      */
24225     /**
24226      * @event loadexception
24227      * Fires when loading fails.
24228      * @param {Roo.JsonView} this
24229      * @param {Object} response The raw Connect response object
24230      */
24231     this.addEvents({
24232         'beforerender' : true,
24233         'load' : true,
24234         'loadexception' : true
24235     });
24236 };
24237 Roo.extend(Roo.JsonView, Roo.View, {
24238     /**
24239      * @type {String} The root property in the loaded JSON object that contains the data
24240      */
24241     jsonRoot : "",
24242
24243     /**
24244      * Refreshes the view.
24245      */
24246     refresh : function(){
24247         this.clearSelections();
24248         this.el.update("");
24249         var html = [];
24250         var o = this.jsonData;
24251         if(o && o.length > 0){
24252             for(var i = 0, len = o.length; i < len; i++){
24253                 var data = this.prepareData(o[i], i, o);
24254                 html[html.length] = this.tpl.apply(data);
24255             }
24256         }else{
24257             html.push(this.emptyText);
24258         }
24259         this.el.update(html.join(""));
24260         this.nodes = this.el.dom.childNodes;
24261         this.updateIndexes(0);
24262     },
24263
24264     /**
24265      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
24266      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
24267      <pre><code>
24268      view.load({
24269          url: "your-url.php",
24270          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24271          callback: yourFunction,
24272          scope: yourObject, //(optional scope)
24273          discardUrl: false,
24274          nocache: false,
24275          text: "Loading...",
24276          timeout: 30,
24277          scripts: false
24278      });
24279      </code></pre>
24280      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24281      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24282      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24283      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24284      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24285      */
24286     load : function(){
24287         var um = this.el.getUpdateManager();
24288         um.update.apply(um, arguments);
24289     },
24290
24291     render : function(el, response){
24292         this.clearSelections();
24293         this.el.update("");
24294         var o;
24295         try{
24296             o = Roo.util.JSON.decode(response.responseText);
24297             if(this.jsonRoot){
24298                 
24299                 o = o[this.jsonRoot];
24300             }
24301         } catch(e){
24302         }
24303         /**
24304          * The current JSON data or null
24305          */
24306         this.jsonData = o;
24307         this.beforeRender();
24308         this.refresh();
24309     },
24310
24311 /**
24312  * Get the number of records in the current JSON dataset
24313  * @return {Number}
24314  */
24315     getCount : function(){
24316         return this.jsonData ? this.jsonData.length : 0;
24317     },
24318
24319 /**
24320  * Returns the JSON object for the specified node(s)
24321  * @param {HTMLElement/Array} node The node or an array of nodes
24322  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24323  * you get the JSON object for the node
24324  */
24325     getNodeData : function(node){
24326         if(node instanceof Array){
24327             var data = [];
24328             for(var i = 0, len = node.length; i < len; i++){
24329                 data.push(this.getNodeData(node[i]));
24330             }
24331             return data;
24332         }
24333         return this.jsonData[this.indexOf(node)] || null;
24334     },
24335
24336     beforeRender : function(){
24337         this.snapshot = this.jsonData;
24338         if(this.sortInfo){
24339             this.sort.apply(this, this.sortInfo);
24340         }
24341         this.fireEvent("beforerender", this, this.jsonData);
24342     },
24343
24344     onLoad : function(el, o){
24345         this.fireEvent("load", this, this.jsonData, o);
24346     },
24347
24348     onLoadException : function(el, o){
24349         this.fireEvent("loadexception", this, o);
24350     },
24351
24352 /**
24353  * Filter the data by a specific property.
24354  * @param {String} property A property on your JSON objects
24355  * @param {String/RegExp} value Either string that the property values
24356  * should start with, or a RegExp to test against the property
24357  */
24358     filter : function(property, value){
24359         if(this.jsonData){
24360             var data = [];
24361             var ss = this.snapshot;
24362             if(typeof value == "string"){
24363                 var vlen = value.length;
24364                 if(vlen == 0){
24365                     this.clearFilter();
24366                     return;
24367                 }
24368                 value = value.toLowerCase();
24369                 for(var i = 0, len = ss.length; i < len; i++){
24370                     var o = ss[i];
24371                     if(o[property].substr(0, vlen).toLowerCase() == value){
24372                         data.push(o);
24373                     }
24374                 }
24375             } else if(value.exec){ // regex?
24376                 for(var i = 0, len = ss.length; i < len; i++){
24377                     var o = ss[i];
24378                     if(value.test(o[property])){
24379                         data.push(o);
24380                     }
24381                 }
24382             } else{
24383                 return;
24384             }
24385             this.jsonData = data;
24386             this.refresh();
24387         }
24388     },
24389
24390 /**
24391  * Filter by a function. The passed function will be called with each
24392  * object in the current dataset. If the function returns true the value is kept,
24393  * otherwise it is filtered.
24394  * @param {Function} fn
24395  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24396  */
24397     filterBy : function(fn, scope){
24398         if(this.jsonData){
24399             var data = [];
24400             var ss = this.snapshot;
24401             for(var i = 0, len = ss.length; i < len; i++){
24402                 var o = ss[i];
24403                 if(fn.call(scope || this, o)){
24404                     data.push(o);
24405                 }
24406             }
24407             this.jsonData = data;
24408             this.refresh();
24409         }
24410     },
24411
24412 /**
24413  * Clears the current filter.
24414  */
24415     clearFilter : function(){
24416         if(this.snapshot && this.jsonData != this.snapshot){
24417             this.jsonData = this.snapshot;
24418             this.refresh();
24419         }
24420     },
24421
24422
24423 /**
24424  * Sorts the data for this view and refreshes it.
24425  * @param {String} property A property on your JSON objects to sort on
24426  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24427  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24428  */
24429     sort : function(property, dir, sortType){
24430         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24431         if(this.jsonData){
24432             var p = property;
24433             var dsc = dir && dir.toLowerCase() == "desc";
24434             var f = function(o1, o2){
24435                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24436                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24437                 ;
24438                 if(v1 < v2){
24439                     return dsc ? +1 : -1;
24440                 } else if(v1 > v2){
24441                     return dsc ? -1 : +1;
24442                 } else{
24443                     return 0;
24444                 }
24445             };
24446             this.jsonData.sort(f);
24447             this.refresh();
24448             if(this.jsonData != this.snapshot){
24449                 this.snapshot.sort(f);
24450             }
24451         }
24452     }
24453 });/*
24454  * Based on:
24455  * Ext JS Library 1.1.1
24456  * Copyright(c) 2006-2007, Ext JS, LLC.
24457  *
24458  * Originally Released Under LGPL - original licence link has changed is not relivant.
24459  *
24460  * Fork - LGPL
24461  * <script type="text/javascript">
24462  */
24463  
24464
24465 /**
24466  * @class Roo.ColorPalette
24467  * @extends Roo.Component
24468  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24469  * Here's an example of typical usage:
24470  * <pre><code>
24471 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24472 cp.render('my-div');
24473
24474 cp.on('select', function(palette, selColor){
24475     // do something with selColor
24476 });
24477 </code></pre>
24478  * @constructor
24479  * Create a new ColorPalette
24480  * @param {Object} config The config object
24481  */
24482 Roo.ColorPalette = function(config){
24483     Roo.ColorPalette.superclass.constructor.call(this, config);
24484     this.addEvents({
24485         /**
24486              * @event select
24487              * Fires when a color is selected
24488              * @param {ColorPalette} this
24489              * @param {String} color The 6-digit color hex code (without the # symbol)
24490              */
24491         select: true
24492     });
24493
24494     if(this.handler){
24495         this.on("select", this.handler, this.scope, true);
24496     }
24497 };
24498 Roo.extend(Roo.ColorPalette, Roo.Component, {
24499     /**
24500      * @cfg {String} itemCls
24501      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24502      */
24503     itemCls : "x-color-palette",
24504     /**
24505      * @cfg {String} value
24506      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24507      * the hex codes are case-sensitive.
24508      */
24509     value : null,
24510     clickEvent:'click',
24511     // private
24512     ctype: "Roo.ColorPalette",
24513
24514     /**
24515      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24516      */
24517     allowReselect : false,
24518
24519     /**
24520      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24521      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24522      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24523      * of colors with the width setting until the box is symmetrical.</p>
24524      * <p>You can override individual colors if needed:</p>
24525      * <pre><code>
24526 var cp = new Roo.ColorPalette();
24527 cp.colors[0] = "FF0000";  // change the first box to red
24528 </code></pre>
24529
24530 Or you can provide a custom array of your own for complete control:
24531 <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors = ["000000", "993300", "333300"];
24534 </code></pre>
24535      * @type Array
24536      */
24537     colors : [
24538         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24539         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24540         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24541         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24542         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24543     ],
24544
24545     // private
24546     onRender : function(container, position){
24547         var t = new Roo.MasterTemplate(
24548             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24549         );
24550         var c = this.colors;
24551         for(var i = 0, len = c.length; i < len; i++){
24552             t.add([c[i]]);
24553         }
24554         var el = document.createElement("div");
24555         el.className = this.itemCls;
24556         t.overwrite(el);
24557         container.dom.insertBefore(el, position);
24558         this.el = Roo.get(el);
24559         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24560         if(this.clickEvent != 'click'){
24561             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24562         }
24563     },
24564
24565     // private
24566     afterRender : function(){
24567         Roo.ColorPalette.superclass.afterRender.call(this);
24568         if(this.value){
24569             var s = this.value;
24570             this.value = null;
24571             this.select(s);
24572         }
24573     },
24574
24575     // private
24576     handleClick : function(e, t){
24577         e.preventDefault();
24578         if(!this.disabled){
24579             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24580             this.select(c.toUpperCase());
24581         }
24582     },
24583
24584     /**
24585      * Selects the specified color in the palette (fires the select event)
24586      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24587      */
24588     select : function(color){
24589         color = color.replace("#", "");
24590         if(color != this.value || this.allowReselect){
24591             var el = this.el;
24592             if(this.value){
24593                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24594             }
24595             el.child("a.color-"+color).addClass("x-color-palette-sel");
24596             this.value = color;
24597             this.fireEvent("select", this, color);
24598         }
24599     }
24600 });/*
24601  * Based on:
24602  * Ext JS Library 1.1.1
24603  * Copyright(c) 2006-2007, Ext JS, LLC.
24604  *
24605  * Originally Released Under LGPL - original licence link has changed is not relivant.
24606  *
24607  * Fork - LGPL
24608  * <script type="text/javascript">
24609  */
24610  
24611 /**
24612  * @class Roo.DatePicker
24613  * @extends Roo.Component
24614  * Simple date picker class.
24615  * @constructor
24616  * Create a new DatePicker
24617  * @param {Object} config The config object
24618  */
24619 Roo.DatePicker = function(config){
24620     Roo.DatePicker.superclass.constructor.call(this, config);
24621
24622     this.value = config && config.value ?
24623                  config.value.clearTime() : new Date().clearTime();
24624
24625     this.addEvents({
24626         /**
24627              * @event select
24628              * Fires when a date is selected
24629              * @param {DatePicker} this
24630              * @param {Date} date The selected date
24631              */
24632         'select': true,
24633         /**
24634              * @event monthchange
24635              * Fires when the displayed month changes 
24636              * @param {DatePicker} this
24637              * @param {Date} date The selected month
24638              */
24639         'monthchange': true
24640     });
24641
24642     if(this.handler){
24643         this.on("select", this.handler,  this.scope || this);
24644     }
24645     // build the disabledDatesRE
24646     if(!this.disabledDatesRE && this.disabledDates){
24647         var dd = this.disabledDates;
24648         var re = "(?:";
24649         for(var i = 0; i < dd.length; i++){
24650             re += dd[i];
24651             if(i != dd.length-1) re += "|";
24652         }
24653         this.disabledDatesRE = new RegExp(re + ")");
24654     }
24655 };
24656
24657 Roo.extend(Roo.DatePicker, Roo.Component, {
24658     /**
24659      * @cfg {String} todayText
24660      * The text to display on the button that selects the current date (defaults to "Today")
24661      */
24662     todayText : "Today",
24663     /**
24664      * @cfg {String} okText
24665      * The text to display on the ok button
24666      */
24667     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24668     /**
24669      * @cfg {String} cancelText
24670      * The text to display on the cancel button
24671      */
24672     cancelText : "Cancel",
24673     /**
24674      * @cfg {String} todayTip
24675      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24676      */
24677     todayTip : "{0} (Spacebar)",
24678     /**
24679      * @cfg {Date} minDate
24680      * Minimum allowable date (JavaScript date object, defaults to null)
24681      */
24682     minDate : null,
24683     /**
24684      * @cfg {Date} maxDate
24685      * Maximum allowable date (JavaScript date object, defaults to null)
24686      */
24687     maxDate : null,
24688     /**
24689      * @cfg {String} minText
24690      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24691      */
24692     minText : "This date is before the minimum date",
24693     /**
24694      * @cfg {String} maxText
24695      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24696      */
24697     maxText : "This date is after the maximum date",
24698     /**
24699      * @cfg {String} format
24700      * The default date format string which can be overriden for localization support.  The format must be
24701      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24702      */
24703     format : "m/d/y",
24704     /**
24705      * @cfg {Array} disabledDays
24706      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24707      */
24708     disabledDays : null,
24709     /**
24710      * @cfg {String} disabledDaysText
24711      * The tooltip to display when the date falls on a disabled day (defaults to "")
24712      */
24713     disabledDaysText : "",
24714     /**
24715      * @cfg {RegExp} disabledDatesRE
24716      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24717      */
24718     disabledDatesRE : null,
24719     /**
24720      * @cfg {String} disabledDatesText
24721      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24722      */
24723     disabledDatesText : "",
24724     /**
24725      * @cfg {Boolean} constrainToViewport
24726      * True to constrain the date picker to the viewport (defaults to true)
24727      */
24728     constrainToViewport : true,
24729     /**
24730      * @cfg {Array} monthNames
24731      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24732      */
24733     monthNames : Date.monthNames,
24734     /**
24735      * @cfg {Array} dayNames
24736      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24737      */
24738     dayNames : Date.dayNames,
24739     /**
24740      * @cfg {String} nextText
24741      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24742      */
24743     nextText: 'Next Month (Control+Right)',
24744     /**
24745      * @cfg {String} prevText
24746      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24747      */
24748     prevText: 'Previous Month (Control+Left)',
24749     /**
24750      * @cfg {String} monthYearText
24751      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24752      */
24753     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24754     /**
24755      * @cfg {Number} startDay
24756      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24757      */
24758     startDay : 0,
24759     /**
24760      * @cfg {Bool} showClear
24761      * Show a clear button (usefull for date form elements that can be blank.)
24762      */
24763     
24764     showClear: false,
24765     
24766     /**
24767      * Sets the value of the date field
24768      * @param {Date} value The date to set
24769      */
24770     setValue : function(value){
24771         var old = this.value;
24772         this.value = value.clearTime(true);
24773         if(this.el){
24774             this.update(this.value);
24775         }
24776     },
24777
24778     /**
24779      * Gets the current selected value of the date field
24780      * @return {Date} The selected date
24781      */
24782     getValue : function(){
24783         return this.value;
24784     },
24785
24786     // private
24787     focus : function(){
24788         if(this.el){
24789             this.update(this.activeDate);
24790         }
24791     },
24792
24793     // private
24794     onRender : function(container, position){
24795         var m = [
24796              '<table cellspacing="0">',
24797                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24798                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24799         var dn = this.dayNames;
24800         for(var i = 0; i < 7; i++){
24801             var d = this.startDay+i;
24802             if(d > 6){
24803                 d = d-7;
24804             }
24805             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24806         }
24807         m[m.length] = "</tr></thead><tbody><tr>";
24808         for(var i = 0; i < 42; i++) {
24809             if(i % 7 == 0 && i != 0){
24810                 m[m.length] = "</tr><tr>";
24811             }
24812             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24813         }
24814         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24815             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24816
24817         var el = document.createElement("div");
24818         el.className = "x-date-picker";
24819         el.innerHTML = m.join("");
24820
24821         container.dom.insertBefore(el, position);
24822
24823         this.el = Roo.get(el);
24824         this.eventEl = Roo.get(el.firstChild);
24825
24826         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24827             handler: this.showPrevMonth,
24828             scope: this,
24829             preventDefault:true,
24830             stopDefault:true
24831         });
24832
24833         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24834             handler: this.showNextMonth,
24835             scope: this,
24836             preventDefault:true,
24837             stopDefault:true
24838         });
24839
24840         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24841
24842         this.monthPicker = this.el.down('div.x-date-mp');
24843         this.monthPicker.enableDisplayMode('block');
24844         
24845         var kn = new Roo.KeyNav(this.eventEl, {
24846             "left" : function(e){
24847                 e.ctrlKey ?
24848                     this.showPrevMonth() :
24849                     this.update(this.activeDate.add("d", -1));
24850             },
24851
24852             "right" : function(e){
24853                 e.ctrlKey ?
24854                     this.showNextMonth() :
24855                     this.update(this.activeDate.add("d", 1));
24856             },
24857
24858             "up" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextYear() :
24861                     this.update(this.activeDate.add("d", -7));
24862             },
24863
24864             "down" : function(e){
24865                 e.ctrlKey ?
24866                     this.showPrevYear() :
24867                     this.update(this.activeDate.add("d", 7));
24868             },
24869
24870             "pageUp" : function(e){
24871                 this.showNextMonth();
24872             },
24873
24874             "pageDown" : function(e){
24875                 this.showPrevMonth();
24876             },
24877
24878             "enter" : function(e){
24879                 e.stopPropagation();
24880                 return true;
24881             },
24882
24883             scope : this
24884         });
24885
24886         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24887
24888         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24889
24890         this.el.unselectable();
24891         
24892         this.cells = this.el.select("table.x-date-inner tbody td");
24893         this.textNodes = this.el.query("table.x-date-inner tbody span");
24894
24895         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24896             text: "&#160;",
24897             tooltip: this.monthYearText
24898         });
24899
24900         this.mbtn.on('click', this.showMonthPicker, this);
24901         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24902
24903
24904         var today = (new Date()).dateFormat(this.format);
24905         
24906         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24907         if (this.showClear) {
24908             baseTb.add( new Roo.Toolbar.Fill());
24909         }
24910         baseTb.add({
24911             text: String.format(this.todayText, today),
24912             tooltip: String.format(this.todayTip, today),
24913             handler: this.selectToday,
24914             scope: this
24915         });
24916         
24917         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24918             
24919         //});
24920         if (this.showClear) {
24921             
24922             baseTb.add( new Roo.Toolbar.Fill());
24923             baseTb.add({
24924                 text: '&#160;',
24925                 cls: 'x-btn-icon x-btn-clear',
24926                 handler: function() {
24927                     //this.value = '';
24928                     this.fireEvent("select", this, '');
24929                 },
24930                 scope: this
24931             });
24932         }
24933         
24934         
24935         if(Roo.isIE){
24936             this.el.repaint();
24937         }
24938         this.update(this.value);
24939     },
24940
24941     createMonthPicker : function(){
24942         if(!this.monthPicker.dom.firstChild){
24943             var buf = ['<table border="0" cellspacing="0">'];
24944             for(var i = 0; i < 6; i++){
24945                 buf.push(
24946                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24947                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24948                     i == 0 ?
24949                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24950                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24951                 );
24952             }
24953             buf.push(
24954                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24955                     this.okText,
24956                     '</button><button type="button" class="x-date-mp-cancel">',
24957                     this.cancelText,
24958                     '</button></td></tr>',
24959                 '</table>'
24960             );
24961             this.monthPicker.update(buf.join(''));
24962             this.monthPicker.on('click', this.onMonthClick, this);
24963             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24964
24965             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24966             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24967
24968             this.mpMonths.each(function(m, a, i){
24969                 i += 1;
24970                 if((i%2) == 0){
24971                     m.dom.xmonth = 5 + Math.round(i * .5);
24972                 }else{
24973                     m.dom.xmonth = Math.round((i-1) * .5);
24974                 }
24975             });
24976         }
24977     },
24978
24979     showMonthPicker : function(){
24980         this.createMonthPicker();
24981         var size = this.el.getSize();
24982         this.monthPicker.setSize(size);
24983         this.monthPicker.child('table').setSize(size);
24984
24985         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24986         this.updateMPMonth(this.mpSelMonth);
24987         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24988         this.updateMPYear(this.mpSelYear);
24989
24990         this.monthPicker.slideIn('t', {duration:.2});
24991     },
24992
24993     updateMPYear : function(y){
24994         this.mpyear = y;
24995         var ys = this.mpYears.elements;
24996         for(var i = 1; i <= 10; i++){
24997             var td = ys[i-1], y2;
24998             if((i%2) == 0){
24999                 y2 = y + Math.round(i * .5);
25000                 td.firstChild.innerHTML = y2;
25001                 td.xyear = y2;
25002             }else{
25003                 y2 = y - (5-Math.round(i * .5));
25004                 td.firstChild.innerHTML = y2;
25005                 td.xyear = y2;
25006             }
25007             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25008         }
25009     },
25010
25011     updateMPMonth : function(sm){
25012         this.mpMonths.each(function(m, a, i){
25013             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         });
25015     },
25016
25017     selectMPMonth: function(m){
25018         
25019     },
25020
25021     onMonthClick : function(e, t){
25022         e.stopEvent();
25023         var el = new Roo.Element(t), pn;
25024         if(el.is('button.x-date-mp-cancel')){
25025             this.hideMonthPicker();
25026         }
25027         else if(el.is('button.x-date-mp-ok')){
25028             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25029             this.hideMonthPicker();
25030         }
25031         else if(pn = el.up('td.x-date-mp-month', 2)){
25032             this.mpMonths.removeClass('x-date-mp-sel');
25033             pn.addClass('x-date-mp-sel');
25034             this.mpSelMonth = pn.dom.xmonth;
25035         }
25036         else if(pn = el.up('td.x-date-mp-year', 2)){
25037             this.mpYears.removeClass('x-date-mp-sel');
25038             pn.addClass('x-date-mp-sel');
25039             this.mpSelYear = pn.dom.xyear;
25040         }
25041         else if(el.is('a.x-date-mp-prev')){
25042             this.updateMPYear(this.mpyear-10);
25043         }
25044         else if(el.is('a.x-date-mp-next')){
25045             this.updateMPYear(this.mpyear+10);
25046         }
25047     },
25048
25049     onMonthDblClick : function(e, t){
25050         e.stopEvent();
25051         var el = new Roo.Element(t), pn;
25052         if(pn = el.up('td.x-date-mp-month', 2)){
25053             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25054             this.hideMonthPicker();
25055         }
25056         else if(pn = el.up('td.x-date-mp-year', 2)){
25057             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25058             this.hideMonthPicker();
25059         }
25060     },
25061
25062     hideMonthPicker : function(disableAnim){
25063         if(this.monthPicker){
25064             if(disableAnim === true){
25065                 this.monthPicker.hide();
25066             }else{
25067                 this.monthPicker.slideOut('t', {duration:.2});
25068             }
25069         }
25070     },
25071
25072     // private
25073     showPrevMonth : function(e){
25074         this.update(this.activeDate.add("mo", -1));
25075     },
25076
25077     // private
25078     showNextMonth : function(e){
25079         this.update(this.activeDate.add("mo", 1));
25080     },
25081
25082     // private
25083     showPrevYear : function(){
25084         this.update(this.activeDate.add("y", -1));
25085     },
25086
25087     // private
25088     showNextYear : function(){
25089         this.update(this.activeDate.add("y", 1));
25090     },
25091
25092     // private
25093     handleMouseWheel : function(e){
25094         var delta = e.getWheelDelta();
25095         if(delta > 0){
25096             this.showPrevMonth();
25097             e.stopEvent();
25098         } else if(delta < 0){
25099             this.showNextMonth();
25100             e.stopEvent();
25101         }
25102     },
25103
25104     // private
25105     handleDateClick : function(e, t){
25106         e.stopEvent();
25107         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25108             this.setValue(new Date(t.dateValue));
25109             this.fireEvent("select", this, this.value);
25110         }
25111     },
25112
25113     // private
25114     selectToday : function(){
25115         this.setValue(new Date().clearTime());
25116         this.fireEvent("select", this, this.value);
25117     },
25118
25119     // private
25120     update : function(date)
25121     {
25122         var vd = this.activeDate;
25123         this.activeDate = date;
25124         if(vd && this.el){
25125             var t = date.getTime();
25126             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25127                 this.cells.removeClass("x-date-selected");
25128                 this.cells.each(function(c){
25129                    if(c.dom.firstChild.dateValue == t){
25130                        c.addClass("x-date-selected");
25131                        setTimeout(function(){
25132                             try{c.dom.firstChild.focus();}catch(e){}
25133                        }, 50);
25134                        return false;
25135                    }
25136                 });
25137                 return;
25138             }
25139         }
25140         
25141         var days = date.getDaysInMonth();
25142         var firstOfMonth = date.getFirstDateOfMonth();
25143         var startingPos = firstOfMonth.getDay()-this.startDay;
25144
25145         if(startingPos <= this.startDay){
25146             startingPos += 7;
25147         }
25148
25149         var pm = date.add("mo", -1);
25150         var prevStart = pm.getDaysInMonth()-startingPos;
25151
25152         var cells = this.cells.elements;
25153         var textEls = this.textNodes;
25154         days += startingPos;
25155
25156         // convert everything to numbers so it's fast
25157         var day = 86400000;
25158         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25159         var today = new Date().clearTime().getTime();
25160         var sel = date.clearTime().getTime();
25161         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25162         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25163         var ddMatch = this.disabledDatesRE;
25164         var ddText = this.disabledDatesText;
25165         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25166         var ddaysText = this.disabledDaysText;
25167         var format = this.format;
25168
25169         var setCellClass = function(cal, cell){
25170             cell.title = "";
25171             var t = d.getTime();
25172             cell.firstChild.dateValue = t;
25173             if(t == today){
25174                 cell.className += " x-date-today";
25175                 cell.title = cal.todayText;
25176             }
25177             if(t == sel){
25178                 cell.className += " x-date-selected";
25179                 setTimeout(function(){
25180                     try{cell.firstChild.focus();}catch(e){}
25181                 }, 50);
25182             }
25183             // disabling
25184             if(t < min) {
25185                 cell.className = " x-date-disabled";
25186                 cell.title = cal.minText;
25187                 return;
25188             }
25189             if(t > max) {
25190                 cell.className = " x-date-disabled";
25191                 cell.title = cal.maxText;
25192                 return;
25193             }
25194             if(ddays){
25195                 if(ddays.indexOf(d.getDay()) != -1){
25196                     cell.title = ddaysText;
25197                     cell.className = " x-date-disabled";
25198                 }
25199             }
25200             if(ddMatch && format){
25201                 var fvalue = d.dateFormat(format);
25202                 if(ddMatch.test(fvalue)){
25203                     cell.title = ddText.replace("%0", fvalue);
25204                     cell.className = " x-date-disabled";
25205                 }
25206             }
25207         };
25208
25209         var i = 0;
25210         for(; i < startingPos; i++) {
25211             textEls[i].innerHTML = (++prevStart);
25212             d.setDate(d.getDate()+1);
25213             cells[i].className = "x-date-prevday";
25214             setCellClass(this, cells[i]);
25215         }
25216         for(; i < days; i++){
25217             intDay = i - startingPos + 1;
25218             textEls[i].innerHTML = (intDay);
25219             d.setDate(d.getDate()+1);
25220             cells[i].className = "x-date-active";
25221             setCellClass(this, cells[i]);
25222         }
25223         var extraDays = 0;
25224         for(; i < 42; i++) {
25225              textEls[i].innerHTML = (++extraDays);
25226              d.setDate(d.getDate()+1);
25227              cells[i].className = "x-date-nextday";
25228              setCellClass(this, cells[i]);
25229         }
25230
25231         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25232         this.fireEvent('monthchange', this, date);
25233         
25234         if(!this.internalRender){
25235             var main = this.el.dom.firstChild;
25236             var w = main.offsetWidth;
25237             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25238             Roo.fly(main).setWidth(w);
25239             this.internalRender = true;
25240             // opera does not respect the auto grow header center column
25241             // then, after it gets a width opera refuses to recalculate
25242             // without a second pass
25243             if(Roo.isOpera && !this.secondPass){
25244                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25245                 this.secondPass = true;
25246                 this.update.defer(10, this, [date]);
25247             }
25248         }
25249         
25250         
25251     }
25252 });        /*
25253  * Based on:
25254  * Ext JS Library 1.1.1
25255  * Copyright(c) 2006-2007, Ext JS, LLC.
25256  *
25257  * Originally Released Under LGPL - original licence link has changed is not relivant.
25258  *
25259  * Fork - LGPL
25260  * <script type="text/javascript">
25261  */
25262 /**
25263  * @class Roo.TabPanel
25264  * @extends Roo.util.Observable
25265  * A lightweight tab container.
25266  * <br><br>
25267  * Usage:
25268  * <pre><code>
25269 // basic tabs 1, built from existing content
25270 var tabs = new Roo.TabPanel("tabs1");
25271 tabs.addTab("script", "View Script");
25272 tabs.addTab("markup", "View Markup");
25273 tabs.activate("script");
25274
25275 // more advanced tabs, built from javascript
25276 var jtabs = new Roo.TabPanel("jtabs");
25277 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25278
25279 // set up the UpdateManager
25280 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25281 var updater = tab2.getUpdateManager();
25282 updater.setDefaultUrl("ajax1.htm");
25283 tab2.on('activate', updater.refresh, updater, true);
25284
25285 // Use setUrl for Ajax loading
25286 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25287 tab3.setUrl("ajax2.htm", null, true);
25288
25289 // Disabled tab
25290 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25291 tab4.disable();
25292
25293 jtabs.activate("jtabs-1");
25294  * </code></pre>
25295  * @constructor
25296  * Create a new TabPanel.
25297  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25298  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25299  */
25300 Roo.TabPanel = function(container, config){
25301     /**
25302     * The container element for this TabPanel.
25303     * @type Roo.Element
25304     */
25305     this.el = Roo.get(container, true);
25306     if(config){
25307         if(typeof config == "boolean"){
25308             this.tabPosition = config ? "bottom" : "top";
25309         }else{
25310             Roo.apply(this, config);
25311         }
25312     }
25313     if(this.tabPosition == "bottom"){
25314         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25315         this.el.addClass("x-tabs-bottom");
25316     }
25317     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25318     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25319     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25320     if(Roo.isIE){
25321         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25322     }
25323     if(this.tabPosition != "bottom"){
25324         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25325          * @type Roo.Element
25326          */
25327         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25328         this.el.addClass("x-tabs-top");
25329     }
25330     this.items = [];
25331
25332     this.bodyEl.setStyle("position", "relative");
25333
25334     this.active = null;
25335     this.activateDelegate = this.activate.createDelegate(this);
25336
25337     this.addEvents({
25338         /**
25339          * @event tabchange
25340          * Fires when the active tab changes
25341          * @param {Roo.TabPanel} this
25342          * @param {Roo.TabPanelItem} activePanel The new active tab
25343          */
25344         "tabchange": true,
25345         /**
25346          * @event beforetabchange
25347          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25348          * @param {Roo.TabPanel} this
25349          * @param {Object} e Set cancel to true on this object to cancel the tab change
25350          * @param {Roo.TabPanelItem} tab The tab being changed to
25351          */
25352         "beforetabchange" : true
25353     });
25354
25355     Roo.EventManager.onWindowResize(this.onResize, this);
25356     this.cpad = this.el.getPadding("lr");
25357     this.hiddenCount = 0;
25358
25359
25360     // toolbar on the tabbar support...
25361     if (this.toolbar) {
25362         var tcfg = this.toolbar;
25363         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25364         this.toolbar = new Roo.Toolbar(tcfg);
25365         if (Roo.isSafari) {
25366             var tbl = tcfg.container.child('table', true);
25367             tbl.setAttribute('width', '100%');
25368         }
25369         
25370     }
25371    
25372
25373
25374     Roo.TabPanel.superclass.constructor.call(this);
25375 };
25376
25377 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25378     /*
25379      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25380      */
25381     tabPosition : "top",
25382     /*
25383      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25384      */
25385     currentTabWidth : 0,
25386     /*
25387      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25388      */
25389     minTabWidth : 40,
25390     /*
25391      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25392      */
25393     maxTabWidth : 250,
25394     /*
25395      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25396      */
25397     preferredTabWidth : 175,
25398     /*
25399      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25400      */
25401     resizeTabs : false,
25402     /*
25403      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25404      */
25405     monitorResize : true,
25406     /*
25407      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25408      */
25409     toolbar : false,
25410
25411     /**
25412      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25413      * @param {String} id The id of the div to use <b>or create</b>
25414      * @param {String} text The text for the tab
25415      * @param {String} content (optional) Content to put in the TabPanelItem body
25416      * @param {Boolean} closable (optional) True to create a close icon on the tab
25417      * @return {Roo.TabPanelItem} The created TabPanelItem
25418      */
25419     addTab : function(id, text, content, closable){
25420         var item = new Roo.TabPanelItem(this, id, text, closable);
25421         this.addTabItem(item);
25422         if(content){
25423             item.setContent(content);
25424         }
25425         return item;
25426     },
25427
25428     /**
25429      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25430      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25431      * @return {Roo.TabPanelItem}
25432      */
25433     getTab : function(id){
25434         return this.items[id];
25435     },
25436
25437     /**
25438      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25439      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25440      */
25441     hideTab : function(id){
25442         var t = this.items[id];
25443         if(!t.isHidden()){
25444            t.setHidden(true);
25445            this.hiddenCount++;
25446            this.autoSizeTabs();
25447         }
25448     },
25449
25450     /**
25451      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25452      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25453      */
25454     unhideTab : function(id){
25455         var t = this.items[id];
25456         if(t.isHidden()){
25457            t.setHidden(false);
25458            this.hiddenCount--;
25459            this.autoSizeTabs();
25460         }
25461     },
25462
25463     /**
25464      * Adds an existing {@link Roo.TabPanelItem}.
25465      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25466      */
25467     addTabItem : function(item){
25468         this.items[item.id] = item;
25469         this.items.push(item);
25470         if(this.resizeTabs){
25471            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25472            this.autoSizeTabs();
25473         }else{
25474             item.autoSize();
25475         }
25476     },
25477
25478     /**
25479      * Removes a {@link Roo.TabPanelItem}.
25480      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25481      */
25482     removeTab : function(id){
25483         var items = this.items;
25484         var tab = items[id];
25485         if(!tab) { return; }
25486         var index = items.indexOf(tab);
25487         if(this.active == tab && items.length > 1){
25488             var newTab = this.getNextAvailable(index);
25489             if(newTab) {
25490                 newTab.activate();
25491             }
25492         }
25493         this.stripEl.dom.removeChild(tab.pnode.dom);
25494         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25495             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25496         }
25497         items.splice(index, 1);
25498         delete this.items[tab.id];
25499         tab.fireEvent("close", tab);
25500         tab.purgeListeners();
25501         this.autoSizeTabs();
25502     },
25503
25504     getNextAvailable : function(start){
25505         var items = this.items;
25506         var index = start;
25507         // look for a next tab that will slide over to
25508         // replace the one being removed
25509         while(index < items.length){
25510             var item = items[++index];
25511             if(item && !item.isHidden()){
25512                 return item;
25513             }
25514         }
25515         // if one isn't found select the previous tab (on the left)
25516         index = start;
25517         while(index >= 0){
25518             var item = items[--index];
25519             if(item && !item.isHidden()){
25520                 return item;
25521             }
25522         }
25523         return null;
25524     },
25525
25526     /**
25527      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25528      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25529      */
25530     disableTab : function(id){
25531         var tab = this.items[id];
25532         if(tab && this.active != tab){
25533             tab.disable();
25534         }
25535     },
25536
25537     /**
25538      * Enables a {@link Roo.TabPanelItem} that is disabled.
25539      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25540      */
25541     enableTab : function(id){
25542         var tab = this.items[id];
25543         tab.enable();
25544     },
25545
25546     /**
25547      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25548      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25549      * @return {Roo.TabPanelItem} The TabPanelItem.
25550      */
25551     activate : function(id){
25552         var tab = this.items[id];
25553         if(!tab){
25554             return null;
25555         }
25556         if(tab == this.active || tab.disabled){
25557             return tab;
25558         }
25559         var e = {};
25560         this.fireEvent("beforetabchange", this, e, tab);
25561         if(e.cancel !== true && !tab.disabled){
25562             if(this.active){
25563                 this.active.hide();
25564             }
25565             this.active = this.items[id];
25566             this.active.show();
25567             this.fireEvent("tabchange", this, this.active);
25568         }
25569         return tab;
25570     },
25571
25572     /**
25573      * Gets the active {@link Roo.TabPanelItem}.
25574      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25575      */
25576     getActiveTab : function(){
25577         return this.active;
25578     },
25579
25580     /**
25581      * Updates the tab body element to fit the height of the container element
25582      * for overflow scrolling
25583      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25584      */
25585     syncHeight : function(targetHeight){
25586         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25587         var bm = this.bodyEl.getMargins();
25588         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25589         this.bodyEl.setHeight(newHeight);
25590         return newHeight;
25591     },
25592
25593     onResize : function(){
25594         if(this.monitorResize){
25595             this.autoSizeTabs();
25596         }
25597     },
25598
25599     /**
25600      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25601      */
25602     beginUpdate : function(){
25603         this.updating = true;
25604     },
25605
25606     /**
25607      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25608      */
25609     endUpdate : function(){
25610         this.updating = false;
25611         this.autoSizeTabs();
25612     },
25613
25614     /**
25615      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25616      */
25617     autoSizeTabs : function(){
25618         var count = this.items.length;
25619         var vcount = count - this.hiddenCount;
25620         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25621         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25622         var availWidth = Math.floor(w / vcount);
25623         var b = this.stripBody;
25624         if(b.getWidth() > w){
25625             var tabs = this.items;
25626             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25627             if(availWidth < this.minTabWidth){
25628                 /*if(!this.sleft){    // incomplete scrolling code
25629                     this.createScrollButtons();
25630                 }
25631                 this.showScroll();
25632                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25633             }
25634         }else{
25635             if(this.currentTabWidth < this.preferredTabWidth){
25636                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25637             }
25638         }
25639     },
25640
25641     /**
25642      * Returns the number of tabs in this TabPanel.
25643      * @return {Number}
25644      */
25645      getCount : function(){
25646          return this.items.length;
25647      },
25648
25649     /**
25650      * Resizes all the tabs to the passed width
25651      * @param {Number} The new width
25652      */
25653     setTabWidth : function(width){
25654         this.currentTabWidth = width;
25655         for(var i = 0, len = this.items.length; i < len; i++) {
25656                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25657         }
25658     },
25659
25660     /**
25661      * Destroys this TabPanel
25662      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25663      */
25664     destroy : function(removeEl){
25665         Roo.EventManager.removeResizeListener(this.onResize, this);
25666         for(var i = 0, len = this.items.length; i < len; i++){
25667             this.items[i].purgeListeners();
25668         }
25669         if(removeEl === true){
25670             this.el.update("");
25671             this.el.remove();
25672         }
25673     }
25674 });
25675
25676 /**
25677  * @class Roo.TabPanelItem
25678  * @extends Roo.util.Observable
25679  * Represents an individual item (tab plus body) in a TabPanel.
25680  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25681  * @param {String} id The id of this TabPanelItem
25682  * @param {String} text The text for the tab of this TabPanelItem
25683  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25684  */
25685 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25686     /**
25687      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25688      * @type Roo.TabPanel
25689      */
25690     this.tabPanel = tabPanel;
25691     /**
25692      * The id for this TabPanelItem
25693      * @type String
25694      */
25695     this.id = id;
25696     /** @private */
25697     this.disabled = false;
25698     /** @private */
25699     this.text = text;
25700     /** @private */
25701     this.loaded = false;
25702     this.closable = closable;
25703
25704     /**
25705      * The body element for this TabPanelItem.
25706      * @type Roo.Element
25707      */
25708     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25709     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25710     this.bodyEl.setStyle("display", "block");
25711     this.bodyEl.setStyle("zoom", "1");
25712     this.hideAction();
25713
25714     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25715     /** @private */
25716     this.el = Roo.get(els.el, true);
25717     this.inner = Roo.get(els.inner, true);
25718     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25719     this.pnode = Roo.get(els.el.parentNode, true);
25720     this.el.on("mousedown", this.onTabMouseDown, this);
25721     this.el.on("click", this.onTabClick, this);
25722     /** @private */
25723     if(closable){
25724         var c = Roo.get(els.close, true);
25725         c.dom.title = this.closeText;
25726         c.addClassOnOver("close-over");
25727         c.on("click", this.closeClick, this);
25728      }
25729
25730     this.addEvents({
25731          /**
25732          * @event activate
25733          * Fires when this tab becomes the active tab.
25734          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25735          * @param {Roo.TabPanelItem} this
25736          */
25737         "activate": true,
25738         /**
25739          * @event beforeclose
25740          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25741          * @param {Roo.TabPanelItem} this
25742          * @param {Object} e Set cancel to true on this object to cancel the close.
25743          */
25744         "beforeclose": true,
25745         /**
25746          * @event close
25747          * Fires when this tab is closed.
25748          * @param {Roo.TabPanelItem} this
25749          */
25750          "close": true,
25751         /**
25752          * @event deactivate
25753          * Fires when this tab is no longer the active tab.
25754          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25755          * @param {Roo.TabPanelItem} this
25756          */
25757          "deactivate" : true
25758     });
25759     this.hidden = false;
25760
25761     Roo.TabPanelItem.superclass.constructor.call(this);
25762 };
25763
25764 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25765     purgeListeners : function(){
25766        Roo.util.Observable.prototype.purgeListeners.call(this);
25767        this.el.removeAllListeners();
25768     },
25769     /**
25770      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25771      */
25772     show : function(){
25773         this.pnode.addClass("on");
25774         this.showAction();
25775         if(Roo.isOpera){
25776             this.tabPanel.stripWrap.repaint();
25777         }
25778         this.fireEvent("activate", this.tabPanel, this);
25779     },
25780
25781     /**
25782      * Returns true if this tab is the active tab.
25783      * @return {Boolean}
25784      */
25785     isActive : function(){
25786         return this.tabPanel.getActiveTab() == this;
25787     },
25788
25789     /**
25790      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25791      */
25792     hide : function(){
25793         this.pnode.removeClass("on");
25794         this.hideAction();
25795         this.fireEvent("deactivate", this.tabPanel, this);
25796     },
25797
25798     hideAction : function(){
25799         this.bodyEl.hide();
25800         this.bodyEl.setStyle("position", "absolute");
25801         this.bodyEl.setLeft("-20000px");
25802         this.bodyEl.setTop("-20000px");
25803     },
25804
25805     showAction : function(){
25806         this.bodyEl.setStyle("position", "relative");
25807         this.bodyEl.setTop("");
25808         this.bodyEl.setLeft("");
25809         this.bodyEl.show();
25810     },
25811
25812     /**
25813      * Set the tooltip for the tab.
25814      * @param {String} tooltip The tab's tooltip
25815      */
25816     setTooltip : function(text){
25817         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25818             this.textEl.dom.qtip = text;
25819             this.textEl.dom.removeAttribute('title');
25820         }else{
25821             this.textEl.dom.title = text;
25822         }
25823     },
25824
25825     onTabClick : function(e){
25826         e.preventDefault();
25827         this.tabPanel.activate(this.id);
25828     },
25829
25830     onTabMouseDown : function(e){
25831         e.preventDefault();
25832         this.tabPanel.activate(this.id);
25833     },
25834
25835     getWidth : function(){
25836         return this.inner.getWidth();
25837     },
25838
25839     setWidth : function(width){
25840         var iwidth = width - this.pnode.getPadding("lr");
25841         this.inner.setWidth(iwidth);
25842         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25843         this.pnode.setWidth(width);
25844     },
25845
25846     /**
25847      * Show or hide the tab
25848      * @param {Boolean} hidden True to hide or false to show.
25849      */
25850     setHidden : function(hidden){
25851         this.hidden = hidden;
25852         this.pnode.setStyle("display", hidden ? "none" : "");
25853     },
25854
25855     /**
25856      * Returns true if this tab is "hidden"
25857      * @return {Boolean}
25858      */
25859     isHidden : function(){
25860         return this.hidden;
25861     },
25862
25863     /**
25864      * Returns the text for this tab
25865      * @return {String}
25866      */
25867     getText : function(){
25868         return this.text;
25869     },
25870
25871     autoSize : function(){
25872         //this.el.beginMeasure();
25873         this.textEl.setWidth(1);
25874         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25875         //this.el.endMeasure();
25876     },
25877
25878     /**
25879      * Sets the text for the tab (Note: this also sets the tooltip text)
25880      * @param {String} text The tab's text and tooltip
25881      */
25882     setText : function(text){
25883         this.text = text;
25884         this.textEl.update(text);
25885         this.setTooltip(text);
25886         if(!this.tabPanel.resizeTabs){
25887             this.autoSize();
25888         }
25889     },
25890     /**
25891      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25892      */
25893     activate : function(){
25894         this.tabPanel.activate(this.id);
25895     },
25896
25897     /**
25898      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25899      */
25900     disable : function(){
25901         if(this.tabPanel.active != this){
25902             this.disabled = true;
25903             this.pnode.addClass("disabled");
25904         }
25905     },
25906
25907     /**
25908      * Enables this TabPanelItem if it was previously disabled.
25909      */
25910     enable : function(){
25911         this.disabled = false;
25912         this.pnode.removeClass("disabled");
25913     },
25914
25915     /**
25916      * Sets the content for this TabPanelItem.
25917      * @param {String} content The content
25918      * @param {Boolean} loadScripts true to look for and load scripts
25919      */
25920     setContent : function(content, loadScripts){
25921         this.bodyEl.update(content, loadScripts);
25922     },
25923
25924     /**
25925      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25926      * @return {Roo.UpdateManager} The UpdateManager
25927      */
25928     getUpdateManager : function(){
25929         return this.bodyEl.getUpdateManager();
25930     },
25931
25932     /**
25933      * Set a URL to be used to load the content for this TabPanelItem.
25934      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25935      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25936      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25937      * @return {Roo.UpdateManager} The UpdateManager
25938      */
25939     setUrl : function(url, params, loadOnce){
25940         if(this.refreshDelegate){
25941             this.un('activate', this.refreshDelegate);
25942         }
25943         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25944         this.on("activate", this.refreshDelegate);
25945         return this.bodyEl.getUpdateManager();
25946     },
25947
25948     /** @private */
25949     _handleRefresh : function(url, params, loadOnce){
25950         if(!loadOnce || !this.loaded){
25951             var updater = this.bodyEl.getUpdateManager();
25952             updater.update(url, params, this._setLoaded.createDelegate(this));
25953         }
25954     },
25955
25956     /**
25957      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25958      *   Will fail silently if the setUrl method has not been called.
25959      *   This does not activate the panel, just updates its content.
25960      */
25961     refresh : function(){
25962         if(this.refreshDelegate){
25963            this.loaded = false;
25964            this.refreshDelegate();
25965         }
25966     },
25967
25968     /** @private */
25969     _setLoaded : function(){
25970         this.loaded = true;
25971     },
25972
25973     /** @private */
25974     closeClick : function(e){
25975         var o = {};
25976         e.stopEvent();
25977         this.fireEvent("beforeclose", this, o);
25978         if(o.cancel !== true){
25979             this.tabPanel.removeTab(this.id);
25980         }
25981     },
25982     /**
25983      * The text displayed in the tooltip for the close icon.
25984      * @type String
25985      */
25986     closeText : "Close this tab"
25987 });
25988
25989 /** @private */
25990 Roo.TabPanel.prototype.createStrip = function(container){
25991     var strip = document.createElement("div");
25992     strip.className = "x-tabs-wrap";
25993     container.appendChild(strip);
25994     return strip;
25995 };
25996 /** @private */
25997 Roo.TabPanel.prototype.createStripList = function(strip){
25998     // div wrapper for retard IE
25999     // returns the "tr" element.
26000     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26001         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26002         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26003     return strip.firstChild.firstChild.firstChild.firstChild;
26004 };
26005 /** @private */
26006 Roo.TabPanel.prototype.createBody = function(container){
26007     var body = document.createElement("div");
26008     Roo.id(body, "tab-body");
26009     Roo.fly(body).addClass("x-tabs-body");
26010     container.appendChild(body);
26011     return body;
26012 };
26013 /** @private */
26014 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26015     var body = Roo.getDom(id);
26016     if(!body){
26017         body = document.createElement("div");
26018         body.id = id;
26019     }
26020     Roo.fly(body).addClass("x-tabs-item-body");
26021     bodyEl.insertBefore(body, bodyEl.firstChild);
26022     return body;
26023 };
26024 /** @private */
26025 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26026     var td = document.createElement("td");
26027     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26028     //stripEl.appendChild(td);
26029     if(closable){
26030         td.className = "x-tabs-closable";
26031         if(!this.closeTpl){
26032             this.closeTpl = new Roo.Template(
26033                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26034                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26035                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26036             );
26037         }
26038         var el = this.closeTpl.overwrite(td, {"text": text});
26039         var close = el.getElementsByTagName("div")[0];
26040         var inner = el.getElementsByTagName("em")[0];
26041         return {"el": el, "close": close, "inner": inner};
26042     } else {
26043         if(!this.tabTpl){
26044             this.tabTpl = new Roo.Template(
26045                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26046                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26047             );
26048         }
26049         var el = this.tabTpl.overwrite(td, {"text": text});
26050         var inner = el.getElementsByTagName("em")[0];
26051         return {"el": el, "inner": inner};
26052     }
26053 };/*
26054  * Based on:
26055  * Ext JS Library 1.1.1
26056  * Copyright(c) 2006-2007, Ext JS, LLC.
26057  *
26058  * Originally Released Under LGPL - original licence link has changed is not relivant.
26059  *
26060  * Fork - LGPL
26061  * <script type="text/javascript">
26062  */
26063
26064 /**
26065  * @class Roo.Button
26066  * @extends Roo.util.Observable
26067  * Simple Button class
26068  * @cfg {String} text The button text
26069  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26070  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26071  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26072  * @cfg {Object} scope The scope of the handler
26073  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26074  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26075  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26076  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26077  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26078  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26079    applies if enableToggle = true)
26080  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26081  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26082   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26083  * @constructor
26084  * Create a new button
26085  * @param {Object} config The config object
26086  */
26087 Roo.Button = function(renderTo, config)
26088 {
26089     if (!config) {
26090         config = renderTo;
26091         renderTo = config.renderTo || false;
26092     }
26093     
26094     Roo.apply(this, config);
26095     this.addEvents({
26096         /**
26097              * @event click
26098              * Fires when this button is clicked
26099              * @param {Button} this
26100              * @param {EventObject} e The click event
26101              */
26102             "click" : true,
26103         /**
26104              * @event toggle
26105              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26106              * @param {Button} this
26107              * @param {Boolean} pressed
26108              */
26109             "toggle" : true,
26110         /**
26111              * @event mouseover
26112              * Fires when the mouse hovers over the button
26113              * @param {Button} this
26114              * @param {Event} e The event object
26115              */
26116         'mouseover' : true,
26117         /**
26118              * @event mouseout
26119              * Fires when the mouse exits the button
26120              * @param {Button} this
26121              * @param {Event} e The event object
26122              */
26123         'mouseout': true,
26124          /**
26125              * @event render
26126              * Fires when the button is rendered
26127              * @param {Button} this
26128              */
26129         'render': true
26130     });
26131     if(this.menu){
26132         this.menu = Roo.menu.MenuMgr.get(this.menu);
26133     }
26134     // register listeners first!!  - so render can be captured..
26135     Roo.util.Observable.call(this);
26136     if(renderTo){
26137         this.render(renderTo);
26138     }
26139     
26140   
26141 };
26142
26143 Roo.extend(Roo.Button, Roo.util.Observable, {
26144     /**
26145      * 
26146      */
26147     
26148     /**
26149      * Read-only. True if this button is hidden
26150      * @type Boolean
26151      */
26152     hidden : false,
26153     /**
26154      * Read-only. True if this button is disabled
26155      * @type Boolean
26156      */
26157     disabled : false,
26158     /**
26159      * Read-only. True if this button is pressed (only if enableToggle = true)
26160      * @type Boolean
26161      */
26162     pressed : false,
26163
26164     /**
26165      * @cfg {Number} tabIndex 
26166      * The DOM tabIndex for this button (defaults to undefined)
26167      */
26168     tabIndex : undefined,
26169
26170     /**
26171      * @cfg {Boolean} enableToggle
26172      * True to enable pressed/not pressed toggling (defaults to false)
26173      */
26174     enableToggle: false,
26175     /**
26176      * @cfg {Mixed} menu
26177      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26178      */
26179     menu : undefined,
26180     /**
26181      * @cfg {String} menuAlign
26182      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26183      */
26184     menuAlign : "tl-bl?",
26185
26186     /**
26187      * @cfg {String} iconCls
26188      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26189      */
26190     iconCls : undefined,
26191     /**
26192      * @cfg {String} type
26193      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26194      */
26195     type : 'button',
26196
26197     // private
26198     menuClassTarget: 'tr',
26199
26200     /**
26201      * @cfg {String} clickEvent
26202      * The type of event to map to the button's event handler (defaults to 'click')
26203      */
26204     clickEvent : 'click',
26205
26206     /**
26207      * @cfg {Boolean} handleMouseEvents
26208      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26209      */
26210     handleMouseEvents : true,
26211
26212     /**
26213      * @cfg {String} tooltipType
26214      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26215      */
26216     tooltipType : 'qtip',
26217
26218     /**
26219      * @cfg {String} cls
26220      * A CSS class to apply to the button's main element.
26221      */
26222     
26223     /**
26224      * @cfg {Roo.Template} template (Optional)
26225      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26226      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26227      * require code modifications if required elements (e.g. a button) aren't present.
26228      */
26229
26230     // private
26231     render : function(renderTo){
26232         var btn;
26233         if(this.hideParent){
26234             this.parentEl = Roo.get(renderTo);
26235         }
26236         if(!this.dhconfig){
26237             if(!this.template){
26238                 if(!Roo.Button.buttonTemplate){
26239                     // hideous table template
26240                     Roo.Button.buttonTemplate = new Roo.Template(
26241                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26242                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
26243                         "</tr></tbody></table>");
26244                 }
26245                 this.template = Roo.Button.buttonTemplate;
26246             }
26247             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26248             var btnEl = btn.child("button:first");
26249             btnEl.on('focus', this.onFocus, this);
26250             btnEl.on('blur', this.onBlur, this);
26251             if(this.cls){
26252                 btn.addClass(this.cls);
26253             }
26254             if(this.icon){
26255                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26256             }
26257             if(this.iconCls){
26258                 btnEl.addClass(this.iconCls);
26259                 if(!this.cls){
26260                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26261                 }
26262             }
26263             if(this.tabIndex !== undefined){
26264                 btnEl.dom.tabIndex = this.tabIndex;
26265             }
26266             if(this.tooltip){
26267                 if(typeof this.tooltip == 'object'){
26268                     Roo.QuickTips.tips(Roo.apply({
26269                           target: btnEl.id
26270                     }, this.tooltip));
26271                 } else {
26272                     btnEl.dom[this.tooltipType] = this.tooltip;
26273                 }
26274             }
26275         }else{
26276             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26277         }
26278         this.el = btn;
26279         if(this.id){
26280             this.el.dom.id = this.el.id = this.id;
26281         }
26282         if(this.menu){
26283             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26284             this.menu.on("show", this.onMenuShow, this);
26285             this.menu.on("hide", this.onMenuHide, this);
26286         }
26287         btn.addClass("x-btn");
26288         if(Roo.isIE && !Roo.isIE7){
26289             this.autoWidth.defer(1, this);
26290         }else{
26291             this.autoWidth();
26292         }
26293         if(this.handleMouseEvents){
26294             btn.on("mouseover", this.onMouseOver, this);
26295             btn.on("mouseout", this.onMouseOut, this);
26296             btn.on("mousedown", this.onMouseDown, this);
26297         }
26298         btn.on(this.clickEvent, this.onClick, this);
26299         //btn.on("mouseup", this.onMouseUp, this);
26300         if(this.hidden){
26301             this.hide();
26302         }
26303         if(this.disabled){
26304             this.disable();
26305         }
26306         Roo.ButtonToggleMgr.register(this);
26307         if(this.pressed){
26308             this.el.addClass("x-btn-pressed");
26309         }
26310         if(this.repeat){
26311             var repeater = new Roo.util.ClickRepeater(btn,
26312                 typeof this.repeat == "object" ? this.repeat : {}
26313             );
26314             repeater.on("click", this.onClick,  this);
26315         }
26316         
26317         this.fireEvent('render', this);
26318         
26319     },
26320     /**
26321      * Returns the button's underlying element
26322      * @return {Roo.Element} The element
26323      */
26324     getEl : function(){
26325         return this.el;  
26326     },
26327     
26328     /**
26329      * Destroys this Button and removes any listeners.
26330      */
26331     destroy : function(){
26332         Roo.ButtonToggleMgr.unregister(this);
26333         this.el.removeAllListeners();
26334         this.purgeListeners();
26335         this.el.remove();
26336     },
26337
26338     // private
26339     autoWidth : function(){
26340         if(this.el){
26341             this.el.setWidth("auto");
26342             if(Roo.isIE7 && Roo.isStrict){
26343                 var ib = this.el.child('button');
26344                 if(ib && ib.getWidth() > 20){
26345                     ib.clip();
26346                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26347                 }
26348             }
26349             if(this.minWidth){
26350                 if(this.hidden){
26351                     this.el.beginMeasure();
26352                 }
26353                 if(this.el.getWidth() < this.minWidth){
26354                     this.el.setWidth(this.minWidth);
26355                 }
26356                 if(this.hidden){
26357                     this.el.endMeasure();
26358                 }
26359             }
26360         }
26361     },
26362
26363     /**
26364      * Assigns this button's click handler
26365      * @param {Function} handler The function to call when the button is clicked
26366      * @param {Object} scope (optional) Scope for the function passed in
26367      */
26368     setHandler : function(handler, scope){
26369         this.handler = handler;
26370         this.scope = scope;  
26371     },
26372     
26373     /**
26374      * Sets this button's text
26375      * @param {String} text The button text
26376      */
26377     setText : function(text){
26378         this.text = text;
26379         if(this.el){
26380             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26381         }
26382         this.autoWidth();
26383     },
26384     
26385     /**
26386      * Gets the text for this button
26387      * @return {String} The button text
26388      */
26389     getText : function(){
26390         return this.text;  
26391     },
26392     
26393     /**
26394      * Show this button
26395      */
26396     show: function(){
26397         this.hidden = false;
26398         if(this.el){
26399             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26400         }
26401     },
26402     
26403     /**
26404      * Hide this button
26405      */
26406     hide: function(){
26407         this.hidden = true;
26408         if(this.el){
26409             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26410         }
26411     },
26412     
26413     /**
26414      * Convenience function for boolean show/hide
26415      * @param {Boolean} visible True to show, false to hide
26416      */
26417     setVisible: function(visible){
26418         if(visible) {
26419             this.show();
26420         }else{
26421             this.hide();
26422         }
26423     },
26424     
26425     /**
26426      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26427      * @param {Boolean} state (optional) Force a particular state
26428      */
26429     toggle : function(state){
26430         state = state === undefined ? !this.pressed : state;
26431         if(state != this.pressed){
26432             if(state){
26433                 this.el.addClass("x-btn-pressed");
26434                 this.pressed = true;
26435                 this.fireEvent("toggle", this, true);
26436             }else{
26437                 this.el.removeClass("x-btn-pressed");
26438                 this.pressed = false;
26439                 this.fireEvent("toggle", this, false);
26440             }
26441             if(this.toggleHandler){
26442                 this.toggleHandler.call(this.scope || this, this, state);
26443             }
26444         }
26445     },
26446     
26447     /**
26448      * Focus the button
26449      */
26450     focus : function(){
26451         this.el.child('button:first').focus();
26452     },
26453     
26454     /**
26455      * Disable this button
26456      */
26457     disable : function(){
26458         if(this.el){
26459             this.el.addClass("x-btn-disabled");
26460         }
26461         this.disabled = true;
26462     },
26463     
26464     /**
26465      * Enable this button
26466      */
26467     enable : function(){
26468         if(this.el){
26469             this.el.removeClass("x-btn-disabled");
26470         }
26471         this.disabled = false;
26472     },
26473
26474     /**
26475      * Convenience function for boolean enable/disable
26476      * @param {Boolean} enabled True to enable, false to disable
26477      */
26478     setDisabled : function(v){
26479         this[v !== true ? "enable" : "disable"]();
26480     },
26481
26482     // private
26483     onClick : function(e){
26484         if(e){
26485             e.preventDefault();
26486         }
26487         if(e.button != 0){
26488             return;
26489         }
26490         if(!this.disabled){
26491             if(this.enableToggle){
26492                 this.toggle();
26493             }
26494             if(this.menu && !this.menu.isVisible()){
26495                 this.menu.show(this.el, this.menuAlign);
26496             }
26497             this.fireEvent("click", this, e);
26498             if(this.handler){
26499                 this.el.removeClass("x-btn-over");
26500                 this.handler.call(this.scope || this, this, e);
26501             }
26502         }
26503     },
26504     // private
26505     onMouseOver : function(e){
26506         if(!this.disabled){
26507             this.el.addClass("x-btn-over");
26508             this.fireEvent('mouseover', this, e);
26509         }
26510     },
26511     // private
26512     onMouseOut : function(e){
26513         if(!e.within(this.el,  true)){
26514             this.el.removeClass("x-btn-over");
26515             this.fireEvent('mouseout', this, e);
26516         }
26517     },
26518     // private
26519     onFocus : function(e){
26520         if(!this.disabled){
26521             this.el.addClass("x-btn-focus");
26522         }
26523     },
26524     // private
26525     onBlur : function(e){
26526         this.el.removeClass("x-btn-focus");
26527     },
26528     // private
26529     onMouseDown : function(e){
26530         if(!this.disabled && e.button == 0){
26531             this.el.addClass("x-btn-click");
26532             Roo.get(document).on('mouseup', this.onMouseUp, this);
26533         }
26534     },
26535     // private
26536     onMouseUp : function(e){
26537         if(e.button == 0){
26538             this.el.removeClass("x-btn-click");
26539             Roo.get(document).un('mouseup', this.onMouseUp, this);
26540         }
26541     },
26542     // private
26543     onMenuShow : function(e){
26544         this.el.addClass("x-btn-menu-active");
26545     },
26546     // private
26547     onMenuHide : function(e){
26548         this.el.removeClass("x-btn-menu-active");
26549     }   
26550 });
26551
26552 // Private utility class used by Button
26553 Roo.ButtonToggleMgr = function(){
26554    var groups = {};
26555    
26556    function toggleGroup(btn, state){
26557        if(state){
26558            var g = groups[btn.toggleGroup];
26559            for(var i = 0, l = g.length; i < l; i++){
26560                if(g[i] != btn){
26561                    g[i].toggle(false);
26562                }
26563            }
26564        }
26565    }
26566    
26567    return {
26568        register : function(btn){
26569            if(!btn.toggleGroup){
26570                return;
26571            }
26572            var g = groups[btn.toggleGroup];
26573            if(!g){
26574                g = groups[btn.toggleGroup] = [];
26575            }
26576            g.push(btn);
26577            btn.on("toggle", toggleGroup);
26578        },
26579        
26580        unregister : function(btn){
26581            if(!btn.toggleGroup){
26582                return;
26583            }
26584            var g = groups[btn.toggleGroup];
26585            if(g){
26586                g.remove(btn);
26587                btn.un("toggle", toggleGroup);
26588            }
26589        }
26590    };
26591 }();/*
26592  * Based on:
26593  * Ext JS Library 1.1.1
26594  * Copyright(c) 2006-2007, Ext JS, LLC.
26595  *
26596  * Originally Released Under LGPL - original licence link has changed is not relivant.
26597  *
26598  * Fork - LGPL
26599  * <script type="text/javascript">
26600  */
26601  
26602 /**
26603  * @class Roo.SplitButton
26604  * @extends Roo.Button
26605  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26606  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26607  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26608  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26609  * @cfg {String} arrowTooltip The title attribute of the arrow
26610  * @constructor
26611  * Create a new menu button
26612  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26613  * @param {Object} config The config object
26614  */
26615 Roo.SplitButton = function(renderTo, config){
26616     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26617     /**
26618      * @event arrowclick
26619      * Fires when this button's arrow is clicked
26620      * @param {SplitButton} this
26621      * @param {EventObject} e The click event
26622      */
26623     this.addEvents({"arrowclick":true});
26624 };
26625
26626 Roo.extend(Roo.SplitButton, Roo.Button, {
26627     render : function(renderTo){
26628         // this is one sweet looking template!
26629         var tpl = new Roo.Template(
26630             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26631             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26632             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26633             "</tbody></table></td><td>",
26634             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26635             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26636             "</tbody></table></td></tr></table>"
26637         );
26638         var btn = tpl.append(renderTo, [this.text, this.type], true);
26639         var btnEl = btn.child("button");
26640         if(this.cls){
26641             btn.addClass(this.cls);
26642         }
26643         if(this.icon){
26644             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26645         }
26646         if(this.iconCls){
26647             btnEl.addClass(this.iconCls);
26648             if(!this.cls){
26649                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26650             }
26651         }
26652         this.el = btn;
26653         if(this.handleMouseEvents){
26654             btn.on("mouseover", this.onMouseOver, this);
26655             btn.on("mouseout", this.onMouseOut, this);
26656             btn.on("mousedown", this.onMouseDown, this);
26657             btn.on("mouseup", this.onMouseUp, this);
26658         }
26659         btn.on(this.clickEvent, this.onClick, this);
26660         if(this.tooltip){
26661             if(typeof this.tooltip == 'object'){
26662                 Roo.QuickTips.tips(Roo.apply({
26663                       target: btnEl.id
26664                 }, this.tooltip));
26665             } else {
26666                 btnEl.dom[this.tooltipType] = this.tooltip;
26667             }
26668         }
26669         if(this.arrowTooltip){
26670             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26671         }
26672         if(this.hidden){
26673             this.hide();
26674         }
26675         if(this.disabled){
26676             this.disable();
26677         }
26678         if(this.pressed){
26679             this.el.addClass("x-btn-pressed");
26680         }
26681         if(Roo.isIE && !Roo.isIE7){
26682             this.autoWidth.defer(1, this);
26683         }else{
26684             this.autoWidth();
26685         }
26686         if(this.menu){
26687             this.menu.on("show", this.onMenuShow, this);
26688             this.menu.on("hide", this.onMenuHide, this);
26689         }
26690         this.fireEvent('render', this);
26691     },
26692
26693     // private
26694     autoWidth : function(){
26695         if(this.el){
26696             var tbl = this.el.child("table:first");
26697             var tbl2 = this.el.child("table:last");
26698             this.el.setWidth("auto");
26699             tbl.setWidth("auto");
26700             if(Roo.isIE7 && Roo.isStrict){
26701                 var ib = this.el.child('button:first');
26702                 if(ib && ib.getWidth() > 20){
26703                     ib.clip();
26704                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26705                 }
26706             }
26707             if(this.minWidth){
26708                 if(this.hidden){
26709                     this.el.beginMeasure();
26710                 }
26711                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26712                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26713                 }
26714                 if(this.hidden){
26715                     this.el.endMeasure();
26716                 }
26717             }
26718             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26719         } 
26720     },
26721     /**
26722      * Sets this button's click handler
26723      * @param {Function} handler The function to call when the button is clicked
26724      * @param {Object} scope (optional) Scope for the function passed above
26725      */
26726     setHandler : function(handler, scope){
26727         this.handler = handler;
26728         this.scope = scope;  
26729     },
26730     
26731     /**
26732      * Sets this button's arrow click handler
26733      * @param {Function} handler The function to call when the arrow is clicked
26734      * @param {Object} scope (optional) Scope for the function passed above
26735      */
26736     setArrowHandler : function(handler, scope){
26737         this.arrowHandler = handler;
26738         this.scope = scope;  
26739     },
26740     
26741     /**
26742      * Focus the button
26743      */
26744     focus : function(){
26745         if(this.el){
26746             this.el.child("button:first").focus();
26747         }
26748     },
26749
26750     // private
26751     onClick : function(e){
26752         e.preventDefault();
26753         if(!this.disabled){
26754             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26755                 if(this.menu && !this.menu.isVisible()){
26756                     this.menu.show(this.el, this.menuAlign);
26757                 }
26758                 this.fireEvent("arrowclick", this, e);
26759                 if(this.arrowHandler){
26760                     this.arrowHandler.call(this.scope || this, this, e);
26761                 }
26762             }else{
26763                 this.fireEvent("click", this, e);
26764                 if(this.handler){
26765                     this.handler.call(this.scope || this, this, e);
26766                 }
26767             }
26768         }
26769     },
26770     // private
26771     onMouseDown : function(e){
26772         if(!this.disabled){
26773             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26774         }
26775     },
26776     // private
26777     onMouseUp : function(e){
26778         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26779     }   
26780 });
26781
26782
26783 // backwards compat
26784 Roo.MenuButton = Roo.SplitButton;/*
26785  * Based on:
26786  * Ext JS Library 1.1.1
26787  * Copyright(c) 2006-2007, Ext JS, LLC.
26788  *
26789  * Originally Released Under LGPL - original licence link has changed is not relivant.
26790  *
26791  * Fork - LGPL
26792  * <script type="text/javascript">
26793  */
26794
26795 /**
26796  * @class Roo.Toolbar
26797  * Basic Toolbar class.
26798  * @constructor
26799  * Creates a new Toolbar
26800  * @param {Object} container The config object
26801  */ 
26802 Roo.Toolbar = function(container, buttons, config)
26803 {
26804     /// old consturctor format still supported..
26805     if(container instanceof Array){ // omit the container for later rendering
26806         buttons = container;
26807         config = buttons;
26808         container = null;
26809     }
26810     if (typeof(container) == 'object' && container.xtype) {
26811         config = container;
26812         container = config.container;
26813         buttons = config.buttons || []; // not really - use items!!
26814     }
26815     var xitems = [];
26816     if (config && config.items) {
26817         xitems = config.items;
26818         delete config.items;
26819     }
26820     Roo.apply(this, config);
26821     this.buttons = buttons;
26822     
26823     if(container){
26824         this.render(container);
26825     }
26826     this.xitems = xitems;
26827     Roo.each(xitems, function(b) {
26828         this.add(b);
26829     }, this);
26830     
26831 };
26832
26833 Roo.Toolbar.prototype = {
26834     /**
26835      * @cfg {Array} items
26836      * array of button configs or elements to add (will be converted to a MixedCollection)
26837      */
26838     
26839     /**
26840      * @cfg {String/HTMLElement/Element} container
26841      * The id or element that will contain the toolbar
26842      */
26843     // private
26844     render : function(ct){
26845         this.el = Roo.get(ct);
26846         if(this.cls){
26847             this.el.addClass(this.cls);
26848         }
26849         // using a table allows for vertical alignment
26850         // 100% width is needed by Safari...
26851         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26852         this.tr = this.el.child("tr", true);
26853         var autoId = 0;
26854         this.items = new Roo.util.MixedCollection(false, function(o){
26855             return o.id || ("item" + (++autoId));
26856         });
26857         if(this.buttons){
26858             this.add.apply(this, this.buttons);
26859             delete this.buttons;
26860         }
26861     },
26862
26863     /**
26864      * Adds element(s) to the toolbar -- this function takes a variable number of 
26865      * arguments of mixed type and adds them to the toolbar.
26866      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26867      * <ul>
26868      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26869      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26870      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26871      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26872      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26873      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26874      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26875      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26876      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26877      * </ul>
26878      * @param {Mixed} arg2
26879      * @param {Mixed} etc.
26880      */
26881     add : function(){
26882         var a = arguments, l = a.length;
26883         for(var i = 0; i < l; i++){
26884             this._add(a[i]);
26885         }
26886     },
26887     // private..
26888     _add : function(el) {
26889         
26890         if (el.xtype) {
26891             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26892         }
26893         
26894         if (el.applyTo){ // some kind of form field
26895             return this.addField(el);
26896         } 
26897         if (el.render){ // some kind of Toolbar.Item
26898             return this.addItem(el);
26899         }
26900         if (typeof el == "string"){ // string
26901             if(el == "separator" || el == "-"){
26902                 return this.addSeparator();
26903             }
26904             if (el == " "){
26905                 return this.addSpacer();
26906             }
26907             if(el == "->"){
26908                 return this.addFill();
26909             }
26910             return this.addText(el);
26911             
26912         }
26913         if(el.tagName){ // element
26914             return this.addElement(el);
26915         }
26916         if(typeof el == "object"){ // must be button config?
26917             return this.addButton(el);
26918         }
26919         // and now what?!?!
26920         return false;
26921         
26922     },
26923     
26924     /**
26925      * Add an Xtype element
26926      * @param {Object} xtype Xtype Object
26927      * @return {Object} created Object
26928      */
26929     addxtype : function(e){
26930         return this.add(e);  
26931     },
26932     
26933     /**
26934      * Returns the Element for this toolbar.
26935      * @return {Roo.Element}
26936      */
26937     getEl : function(){
26938         return this.el;  
26939     },
26940     
26941     /**
26942      * Adds a separator
26943      * @return {Roo.Toolbar.Item} The separator item
26944      */
26945     addSeparator : function(){
26946         return this.addItem(new Roo.Toolbar.Separator());
26947     },
26948
26949     /**
26950      * Adds a spacer element
26951      * @return {Roo.Toolbar.Spacer} The spacer item
26952      */
26953     addSpacer : function(){
26954         return this.addItem(new Roo.Toolbar.Spacer());
26955     },
26956
26957     /**
26958      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26959      * @return {Roo.Toolbar.Fill} The fill item
26960      */
26961     addFill : function(){
26962         return this.addItem(new Roo.Toolbar.Fill());
26963     },
26964
26965     /**
26966      * Adds any standard HTML element to the toolbar
26967      * @param {String/HTMLElement/Element} el The element or id of the element to add
26968      * @return {Roo.Toolbar.Item} The element's item
26969      */
26970     addElement : function(el){
26971         return this.addItem(new Roo.Toolbar.Item(el));
26972     },
26973     /**
26974      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26975      * @type Roo.util.MixedCollection  
26976      */
26977     items : false,
26978      
26979     /**
26980      * Adds any Toolbar.Item or subclass
26981      * @param {Roo.Toolbar.Item} item
26982      * @return {Roo.Toolbar.Item} The item
26983      */
26984     addItem : function(item){
26985         var td = this.nextBlock();
26986         item.render(td);
26987         this.items.add(item);
26988         return item;
26989     },
26990     
26991     /**
26992      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26993      * @param {Object/Array} config A button config or array of configs
26994      * @return {Roo.Toolbar.Button/Array}
26995      */
26996     addButton : function(config){
26997         if(config instanceof Array){
26998             var buttons = [];
26999             for(var i = 0, len = config.length; i < len; i++) {
27000                 buttons.push(this.addButton(config[i]));
27001             }
27002             return buttons;
27003         }
27004         var b = config;
27005         if(!(config instanceof Roo.Toolbar.Button)){
27006             b = config.split ?
27007                 new Roo.Toolbar.SplitButton(config) :
27008                 new Roo.Toolbar.Button(config);
27009         }
27010         var td = this.nextBlock();
27011         b.render(td);
27012         this.items.add(b);
27013         return b;
27014     },
27015     
27016     /**
27017      * Adds text to the toolbar
27018      * @param {String} text The text to add
27019      * @return {Roo.Toolbar.Item} The element's item
27020      */
27021     addText : function(text){
27022         return this.addItem(new Roo.Toolbar.TextItem(text));
27023     },
27024     
27025     /**
27026      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27027      * @param {Number} index The index where the item is to be inserted
27028      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27029      * @return {Roo.Toolbar.Button/Item}
27030      */
27031     insertButton : function(index, item){
27032         if(item instanceof Array){
27033             var buttons = [];
27034             for(var i = 0, len = item.length; i < len; i++) {
27035                buttons.push(this.insertButton(index + i, item[i]));
27036             }
27037             return buttons;
27038         }
27039         if (!(item instanceof Roo.Toolbar.Button)){
27040            item = new Roo.Toolbar.Button(item);
27041         }
27042         var td = document.createElement("td");
27043         this.tr.insertBefore(td, this.tr.childNodes[index]);
27044         item.render(td);
27045         this.items.insert(index, item);
27046         return item;
27047     },
27048     
27049     /**
27050      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27051      * @param {Object} config
27052      * @return {Roo.Toolbar.Item} The element's item
27053      */
27054     addDom : function(config, returnEl){
27055         var td = this.nextBlock();
27056         Roo.DomHelper.overwrite(td, config);
27057         var ti = new Roo.Toolbar.Item(td.firstChild);
27058         ti.render(td);
27059         this.items.add(ti);
27060         return ti;
27061     },
27062
27063     /**
27064      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27065      * @type Roo.util.MixedCollection  
27066      */
27067     fields : false,
27068     
27069     /**
27070      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27071      * Note: the field should not have been rendered yet. For a field that has already been
27072      * rendered, use {@link #addElement}.
27073      * @param {Roo.form.Field} field
27074      * @return {Roo.ToolbarItem}
27075      */
27076      
27077       
27078     addField : function(field) {
27079         if (!this.fields) {
27080             var autoId = 0;
27081             this.fields = new Roo.util.MixedCollection(false, function(o){
27082                 return o.id || ("item" + (++autoId));
27083             });
27084
27085         }
27086         
27087         var td = this.nextBlock();
27088         field.render(td);
27089         var ti = new Roo.Toolbar.Item(td.firstChild);
27090         ti.render(td);
27091         this.items.add(ti);
27092         this.fields.add(field);
27093         return ti;
27094     },
27095     /**
27096      * Hide the toolbar
27097      * @method hide
27098      */
27099      
27100       
27101     hide : function()
27102     {
27103         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27104         this.el.child('div').hide();
27105     },
27106     /**
27107      * Show the toolbar
27108      * @method show
27109      */
27110     show : function()
27111     {
27112         this.el.child('div').show();
27113     },
27114       
27115     // private
27116     nextBlock : function(){
27117         var td = document.createElement("td");
27118         this.tr.appendChild(td);
27119         return td;
27120     },
27121
27122     // private
27123     destroy : function(){
27124         if(this.items){ // rendered?
27125             Roo.destroy.apply(Roo, this.items.items);
27126         }
27127         if(this.fields){ // rendered?
27128             Roo.destroy.apply(Roo, this.fields.items);
27129         }
27130         Roo.Element.uncache(this.el, this.tr);
27131     }
27132 };
27133
27134 /**
27135  * @class Roo.Toolbar.Item
27136  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27137  * @constructor
27138  * Creates a new Item
27139  * @param {HTMLElement} el 
27140  */
27141 Roo.Toolbar.Item = function(el){
27142     this.el = Roo.getDom(el);
27143     this.id = Roo.id(this.el);
27144     this.hidden = false;
27145 };
27146
27147 Roo.Toolbar.Item.prototype = {
27148     
27149     /**
27150      * Get this item's HTML Element
27151      * @return {HTMLElement}
27152      */
27153     getEl : function(){
27154        return this.el;  
27155     },
27156
27157     // private
27158     render : function(td){
27159         this.td = td;
27160         td.appendChild(this.el);
27161     },
27162     
27163     /**
27164      * Removes and destroys this item.
27165      */
27166     destroy : function(){
27167         this.td.parentNode.removeChild(this.td);
27168     },
27169     
27170     /**
27171      * Shows this item.
27172      */
27173     show: function(){
27174         this.hidden = false;
27175         this.td.style.display = "";
27176     },
27177     
27178     /**
27179      * Hides this item.
27180      */
27181     hide: function(){
27182         this.hidden = true;
27183         this.td.style.display = "none";
27184     },
27185     
27186     /**
27187      * Convenience function for boolean show/hide.
27188      * @param {Boolean} visible true to show/false to hide
27189      */
27190     setVisible: function(visible){
27191         if(visible) {
27192             this.show();
27193         }else{
27194             this.hide();
27195         }
27196     },
27197     
27198     /**
27199      * Try to focus this item.
27200      */
27201     focus : function(){
27202         Roo.fly(this.el).focus();
27203     },
27204     
27205     /**
27206      * Disables this item.
27207      */
27208     disable : function(){
27209         Roo.fly(this.td).addClass("x-item-disabled");
27210         this.disabled = true;
27211         this.el.disabled = true;
27212     },
27213     
27214     /**
27215      * Enables this item.
27216      */
27217     enable : function(){
27218         Roo.fly(this.td).removeClass("x-item-disabled");
27219         this.disabled = false;
27220         this.el.disabled = false;
27221     }
27222 };
27223
27224
27225 /**
27226  * @class Roo.Toolbar.Separator
27227  * @extends Roo.Toolbar.Item
27228  * A simple toolbar separator class
27229  * @constructor
27230  * Creates a new Separator
27231  */
27232 Roo.Toolbar.Separator = function(){
27233     var s = document.createElement("span");
27234     s.className = "ytb-sep";
27235     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27236 };
27237 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27238     enable:Roo.emptyFn,
27239     disable:Roo.emptyFn,
27240     focus:Roo.emptyFn
27241 });
27242
27243 /**
27244  * @class Roo.Toolbar.Spacer
27245  * @extends Roo.Toolbar.Item
27246  * A simple element that adds extra horizontal space to a toolbar.
27247  * @constructor
27248  * Creates a new Spacer
27249  */
27250 Roo.Toolbar.Spacer = function(){
27251     var s = document.createElement("div");
27252     s.className = "ytb-spacer";
27253     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27254 };
27255 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27256     enable:Roo.emptyFn,
27257     disable:Roo.emptyFn,
27258     focus:Roo.emptyFn
27259 });
27260
27261 /**
27262  * @class Roo.Toolbar.Fill
27263  * @extends Roo.Toolbar.Spacer
27264  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27265  * @constructor
27266  * Creates a new Spacer
27267  */
27268 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27269     // private
27270     render : function(td){
27271         td.style.width = '100%';
27272         Roo.Toolbar.Fill.superclass.render.call(this, td);
27273     }
27274 });
27275
27276 /**
27277  * @class Roo.Toolbar.TextItem
27278  * @extends Roo.Toolbar.Item
27279  * A simple class that renders text directly into a toolbar.
27280  * @constructor
27281  * Creates a new TextItem
27282  * @param {String} text
27283  */
27284 Roo.Toolbar.TextItem = function(text){
27285     if (typeof(text) == 'object') {
27286         text = text.text;
27287     }
27288     var s = document.createElement("span");
27289     s.className = "ytb-text";
27290     s.innerHTML = text;
27291     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27292 };
27293 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27294     enable:Roo.emptyFn,
27295     disable:Roo.emptyFn,
27296     focus:Roo.emptyFn
27297 });
27298
27299 /**
27300  * @class Roo.Toolbar.Button
27301  * @extends Roo.Button
27302  * A button that renders into a toolbar.
27303  * @constructor
27304  * Creates a new Button
27305  * @param {Object} config A standard {@link Roo.Button} config object
27306  */
27307 Roo.Toolbar.Button = function(config){
27308     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27309 };
27310 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27311     render : function(td){
27312         this.td = td;
27313         Roo.Toolbar.Button.superclass.render.call(this, td);
27314     },
27315     
27316     /**
27317      * Removes and destroys this button
27318      */
27319     destroy : function(){
27320         Roo.Toolbar.Button.superclass.destroy.call(this);
27321         this.td.parentNode.removeChild(this.td);
27322     },
27323     
27324     /**
27325      * Shows this button
27326      */
27327     show: function(){
27328         this.hidden = false;
27329         this.td.style.display = "";
27330     },
27331     
27332     /**
27333      * Hides this button
27334      */
27335     hide: function(){
27336         this.hidden = true;
27337         this.td.style.display = "none";
27338     },
27339
27340     /**
27341      * Disables this item
27342      */
27343     disable : function(){
27344         Roo.fly(this.td).addClass("x-item-disabled");
27345         this.disabled = true;
27346     },
27347
27348     /**
27349      * Enables this item
27350      */
27351     enable : function(){
27352         Roo.fly(this.td).removeClass("x-item-disabled");
27353         this.disabled = false;
27354     }
27355 });
27356 // backwards compat
27357 Roo.ToolbarButton = Roo.Toolbar.Button;
27358
27359 /**
27360  * @class Roo.Toolbar.SplitButton
27361  * @extends Roo.SplitButton
27362  * A menu button that renders into a toolbar.
27363  * @constructor
27364  * Creates a new SplitButton
27365  * @param {Object} config A standard {@link Roo.SplitButton} config object
27366  */
27367 Roo.Toolbar.SplitButton = function(config){
27368     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27369 };
27370 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27371     render : function(td){
27372         this.td = td;
27373         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27374     },
27375     
27376     /**
27377      * Removes and destroys this button
27378      */
27379     destroy : function(){
27380         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27381         this.td.parentNode.removeChild(this.td);
27382     },
27383     
27384     /**
27385      * Shows this button
27386      */
27387     show: function(){
27388         this.hidden = false;
27389         this.td.style.display = "";
27390     },
27391     
27392     /**
27393      * Hides this button
27394      */
27395     hide: function(){
27396         this.hidden = true;
27397         this.td.style.display = "none";
27398     }
27399 });
27400
27401 // backwards compat
27402 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27403  * Based on:
27404  * Ext JS Library 1.1.1
27405  * Copyright(c) 2006-2007, Ext JS, LLC.
27406  *
27407  * Originally Released Under LGPL - original licence link has changed is not relivant.
27408  *
27409  * Fork - LGPL
27410  * <script type="text/javascript">
27411  */
27412  
27413 /**
27414  * @class Roo.PagingToolbar
27415  * @extends Roo.Toolbar
27416  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27417  * @constructor
27418  * Create a new PagingToolbar
27419  * @param {Object} config The config object
27420  */
27421 Roo.PagingToolbar = function(el, ds, config)
27422 {
27423     // old args format still supported... - xtype is prefered..
27424     if (typeof(el) == 'object' && el.xtype) {
27425         // created from xtype...
27426         config = el;
27427         ds = el.dataSource;
27428         el = config.container;
27429     }
27430     var items = [];
27431     if (config.items) {
27432         items = config.items;
27433         config.items = [];
27434     }
27435     
27436     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27437     this.ds = ds;
27438     this.cursor = 0;
27439     this.renderButtons(this.el);
27440     this.bind(ds);
27441     
27442     // supprot items array.
27443    
27444     Roo.each(items, function(e) {
27445         this.add(Roo.factory(e));
27446     },this);
27447     
27448 };
27449
27450 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27451     /**
27452      * @cfg {Roo.data.Store} dataSource
27453      * The underlying data store providing the paged data
27454      */
27455     /**
27456      * @cfg {String/HTMLElement/Element} container
27457      * container The id or element that will contain the toolbar
27458      */
27459     /**
27460      * @cfg {Boolean} displayInfo
27461      * True to display the displayMsg (defaults to false)
27462      */
27463     /**
27464      * @cfg {Number} pageSize
27465      * The number of records to display per page (defaults to 20)
27466      */
27467     pageSize: 20,
27468     /**
27469      * @cfg {String} displayMsg
27470      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27471      */
27472     displayMsg : 'Displaying {0} - {1} of {2}',
27473     /**
27474      * @cfg {String} emptyMsg
27475      * The message to display when no records are found (defaults to "No data to display")
27476      */
27477     emptyMsg : 'No data to display',
27478     /**
27479      * Customizable piece of the default paging text (defaults to "Page")
27480      * @type String
27481      */
27482     beforePageText : "Page",
27483     /**
27484      * Customizable piece of the default paging text (defaults to "of %0")
27485      * @type String
27486      */
27487     afterPageText : "of {0}",
27488     /**
27489      * Customizable piece of the default paging text (defaults to "First Page")
27490      * @type String
27491      */
27492     firstText : "First Page",
27493     /**
27494      * Customizable piece of the default paging text (defaults to "Previous Page")
27495      * @type String
27496      */
27497     prevText : "Previous Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "Next Page")
27500      * @type String
27501      */
27502     nextText : "Next Page",
27503     /**
27504      * Customizable piece of the default paging text (defaults to "Last Page")
27505      * @type String
27506      */
27507     lastText : "Last Page",
27508     /**
27509      * Customizable piece of the default paging text (defaults to "Refresh")
27510      * @type String
27511      */
27512     refreshText : "Refresh",
27513
27514     // private
27515     renderButtons : function(el){
27516         Roo.PagingToolbar.superclass.render.call(this, el);
27517         this.first = this.addButton({
27518             tooltip: this.firstText,
27519             cls: "x-btn-icon x-grid-page-first",
27520             disabled: true,
27521             handler: this.onClick.createDelegate(this, ["first"])
27522         });
27523         this.prev = this.addButton({
27524             tooltip: this.prevText,
27525             cls: "x-btn-icon x-grid-page-prev",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["prev"])
27528         });
27529         //this.addSeparator();
27530         this.add(this.beforePageText);
27531         this.field = Roo.get(this.addDom({
27532            tag: "input",
27533            type: "text",
27534            size: "3",
27535            value: "1",
27536            cls: "x-grid-page-number"
27537         }).el);
27538         this.field.on("keydown", this.onPagingKeydown, this);
27539         this.field.on("focus", function(){this.dom.select();});
27540         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27541         this.field.setHeight(18);
27542         //this.addSeparator();
27543         this.next = this.addButton({
27544             tooltip: this.nextText,
27545             cls: "x-btn-icon x-grid-page-next",
27546             disabled: true,
27547             handler: this.onClick.createDelegate(this, ["next"])
27548         });
27549         this.last = this.addButton({
27550             tooltip: this.lastText,
27551             cls: "x-btn-icon x-grid-page-last",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["last"])
27554         });
27555         //this.addSeparator();
27556         this.loading = this.addButton({
27557             tooltip: this.refreshText,
27558             cls: "x-btn-icon x-grid-loading",
27559             handler: this.onClick.createDelegate(this, ["refresh"])
27560         });
27561
27562         if(this.displayInfo){
27563             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27564         }
27565     },
27566
27567     // private
27568     updateInfo : function(){
27569         if(this.displayEl){
27570             var count = this.ds.getCount();
27571             var msg = count == 0 ?
27572                 this.emptyMsg :
27573                 String.format(
27574                     this.displayMsg,
27575                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27576                 );
27577             this.displayEl.update(msg);
27578         }
27579     },
27580
27581     // private
27582     onLoad : function(ds, r, o){
27583        this.cursor = o.params ? o.params.start : 0;
27584        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27585
27586        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27587        this.field.dom.value = ap;
27588        this.first.setDisabled(ap == 1);
27589        this.prev.setDisabled(ap == 1);
27590        this.next.setDisabled(ap == ps);
27591        this.last.setDisabled(ap == ps);
27592        this.loading.enable();
27593        this.updateInfo();
27594     },
27595
27596     // private
27597     getPageData : function(){
27598         var total = this.ds.getTotalCount();
27599         return {
27600             total : total,
27601             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27602             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27603         };
27604     },
27605
27606     // private
27607     onLoadError : function(){
27608         this.loading.enable();
27609     },
27610
27611     // private
27612     onPagingKeydown : function(e){
27613         var k = e.getKey();
27614         var d = this.getPageData();
27615         if(k == e.RETURN){
27616             var v = this.field.dom.value, pageNum;
27617             if(!v || isNaN(pageNum = parseInt(v, 10))){
27618                 this.field.dom.value = d.activePage;
27619                 return;
27620             }
27621             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27623             e.stopEvent();
27624         }
27625         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27626         {
27627           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27628           this.field.dom.value = pageNum;
27629           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27630           e.stopEvent();
27631         }
27632         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27633         {
27634           var v = this.field.dom.value, pageNum; 
27635           var increment = (e.shiftKey) ? 10 : 1;
27636           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27637             increment *= -1;
27638           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27639             this.field.dom.value = d.activePage;
27640             return;
27641           }
27642           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27643           {
27644             this.field.dom.value = parseInt(v, 10) + increment;
27645             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27646             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27647           }
27648           e.stopEvent();
27649         }
27650     },
27651
27652     // private
27653     beforeLoad : function(){
27654         if(this.loading){
27655             this.loading.disable();
27656         }
27657     },
27658
27659     // private
27660     onClick : function(which){
27661         var ds = this.ds;
27662         switch(which){
27663             case "first":
27664                 ds.load({params:{start: 0, limit: this.pageSize}});
27665             break;
27666             case "prev":
27667                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27668             break;
27669             case "next":
27670                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27671             break;
27672             case "last":
27673                 var total = ds.getTotalCount();
27674                 var extra = total % this.pageSize;
27675                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27676                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27677             break;
27678             case "refresh":
27679                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27680             break;
27681         }
27682     },
27683
27684     /**
27685      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27686      * @param {Roo.data.Store} store The data store to unbind
27687      */
27688     unbind : function(ds){
27689         ds.un("beforeload", this.beforeLoad, this);
27690         ds.un("load", this.onLoad, this);
27691         ds.un("loadexception", this.onLoadError, this);
27692         ds.un("remove", this.updateInfo, this);
27693         ds.un("add", this.updateInfo, this);
27694         this.ds = undefined;
27695     },
27696
27697     /**
27698      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27699      * @param {Roo.data.Store} store The data store to bind
27700      */
27701     bind : function(ds){
27702         ds.on("beforeload", this.beforeLoad, this);
27703         ds.on("load", this.onLoad, this);
27704         ds.on("loadexception", this.onLoadError, this);
27705         ds.on("remove", this.updateInfo, this);
27706         ds.on("add", this.updateInfo, this);
27707         this.ds = ds;
27708     }
27709 });/*
27710  * Based on:
27711  * Ext JS Library 1.1.1
27712  * Copyright(c) 2006-2007, Ext JS, LLC.
27713  *
27714  * Originally Released Under LGPL - original licence link has changed is not relivant.
27715  *
27716  * Fork - LGPL
27717  * <script type="text/javascript">
27718  */
27719
27720 /**
27721  * @class Roo.Resizable
27722  * @extends Roo.util.Observable
27723  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27724  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27725  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27726  * the element will be wrapped for you automatically.</p>
27727  * <p>Here is the list of valid resize handles:</p>
27728  * <pre>
27729 Value   Description
27730 ------  -------------------
27731  'n'     north
27732  's'     south
27733  'e'     east
27734  'w'     west
27735  'nw'    northwest
27736  'sw'    southwest
27737  'se'    southeast
27738  'ne'    northeast
27739  'hd'    horizontal drag
27740  'all'   all
27741 </pre>
27742  * <p>Here's an example showing the creation of a typical Resizable:</p>
27743  * <pre><code>
27744 var resizer = new Roo.Resizable("element-id", {
27745     handles: 'all',
27746     minWidth: 200,
27747     minHeight: 100,
27748     maxWidth: 500,
27749     maxHeight: 400,
27750     pinned: true
27751 });
27752 resizer.on("resize", myHandler);
27753 </code></pre>
27754  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27755  * resizer.east.setDisplayed(false);</p>
27756  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27757  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27758  * resize operation's new size (defaults to [0, 0])
27759  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27760  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27761  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27762  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27763  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27764  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27765  * @cfg {Number} width The width of the element in pixels (defaults to null)
27766  * @cfg {Number} height The height of the element in pixels (defaults to null)
27767  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27768  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27769  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27770  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27771  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27772  * in favor of the handles config option (defaults to false)
27773  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27774  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27775  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27776  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27777  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27778  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27779  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27780  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27781  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27782  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27783  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27784  * @constructor
27785  * Create a new resizable component
27786  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27787  * @param {Object} config configuration options
27788   */
27789 Roo.Resizable = function(el, config)
27790 {
27791     this.el = Roo.get(el);
27792
27793     if(config && config.wrap){
27794         config.resizeChild = this.el;
27795         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27796         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27797         this.el.setStyle("overflow", "hidden");
27798         this.el.setPositioning(config.resizeChild.getPositioning());
27799         config.resizeChild.clearPositioning();
27800         if(!config.width || !config.height){
27801             var csize = config.resizeChild.getSize();
27802             this.el.setSize(csize.width, csize.height);
27803         }
27804         if(config.pinned && !config.adjustments){
27805             config.adjustments = "auto";
27806         }
27807     }
27808
27809     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27810     this.proxy.unselectable();
27811     this.proxy.enableDisplayMode('block');
27812
27813     Roo.apply(this, config);
27814
27815     if(this.pinned){
27816         this.disableTrackOver = true;
27817         this.el.addClass("x-resizable-pinned");
27818     }
27819     // if the element isn't positioned, make it relative
27820     var position = this.el.getStyle("position");
27821     if(position != "absolute" && position != "fixed"){
27822         this.el.setStyle("position", "relative");
27823     }
27824     if(!this.handles){ // no handles passed, must be legacy style
27825         this.handles = 's,e,se';
27826         if(this.multiDirectional){
27827             this.handles += ',n,w';
27828         }
27829     }
27830     if(this.handles == "all"){
27831         this.handles = "n s e w ne nw se sw";
27832     }
27833     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27834     var ps = Roo.Resizable.positions;
27835     for(var i = 0, len = hs.length; i < len; i++){
27836         if(hs[i] && ps[hs[i]]){
27837             var pos = ps[hs[i]];
27838             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27839         }
27840     }
27841     // legacy
27842     this.corner = this.southeast;
27843     
27844     // updateBox = the box can move..
27845     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27846         this.updateBox = true;
27847     }
27848
27849     this.activeHandle = null;
27850
27851     if(this.resizeChild){
27852         if(typeof this.resizeChild == "boolean"){
27853             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27854         }else{
27855             this.resizeChild = Roo.get(this.resizeChild, true);
27856         }
27857     }
27858     
27859     if(this.adjustments == "auto"){
27860         var rc = this.resizeChild;
27861         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27862         if(rc && (hw || hn)){
27863             rc.position("relative");
27864             rc.setLeft(hw ? hw.el.getWidth() : 0);
27865             rc.setTop(hn ? hn.el.getHeight() : 0);
27866         }
27867         this.adjustments = [
27868             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27869             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27870         ];
27871     }
27872
27873     if(this.draggable){
27874         this.dd = this.dynamic ?
27875             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27876         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27877     }
27878
27879     // public events
27880     this.addEvents({
27881         /**
27882          * @event beforeresize
27883          * Fired before resize is allowed. Set enabled to false to cancel resize.
27884          * @param {Roo.Resizable} this
27885          * @param {Roo.EventObject} e The mousedown event
27886          */
27887         "beforeresize" : true,
27888         /**
27889          * @event resize
27890          * Fired after a resize.
27891          * @param {Roo.Resizable} this
27892          * @param {Number} width The new width
27893          * @param {Number} height The new height
27894          * @param {Roo.EventObject} e The mouseup event
27895          */
27896         "resize" : true
27897     });
27898
27899     if(this.width !== null && this.height !== null){
27900         this.resizeTo(this.width, this.height);
27901     }else{
27902         this.updateChildSize();
27903     }
27904     if(Roo.isIE){
27905         this.el.dom.style.zoom = 1;
27906     }
27907     Roo.Resizable.superclass.constructor.call(this);
27908 };
27909
27910 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27911         resizeChild : false,
27912         adjustments : [0, 0],
27913         minWidth : 5,
27914         minHeight : 5,
27915         maxWidth : 10000,
27916         maxHeight : 10000,
27917         enabled : true,
27918         animate : false,
27919         duration : .35,
27920         dynamic : false,
27921         handles : false,
27922         multiDirectional : false,
27923         disableTrackOver : false,
27924         easing : 'easeOutStrong',
27925         widthIncrement : 0,
27926         heightIncrement : 0,
27927         pinned : false,
27928         width : null,
27929         height : null,
27930         preserveRatio : false,
27931         transparent: false,
27932         minX: 0,
27933         minY: 0,
27934         draggable: false,
27935
27936         /**
27937          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27938          */
27939         constrainTo: undefined,
27940         /**
27941          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27942          */
27943         resizeRegion: undefined,
27944
27945
27946     /**
27947      * Perform a manual resize
27948      * @param {Number} width
27949      * @param {Number} height
27950      */
27951     resizeTo : function(width, height){
27952         this.el.setSize(width, height);
27953         this.updateChildSize();
27954         this.fireEvent("resize", this, width, height, null);
27955     },
27956
27957     // private
27958     startSizing : function(e, handle){
27959         this.fireEvent("beforeresize", this, e);
27960         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27961
27962             if(!this.overlay){
27963                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27964                 this.overlay.unselectable();
27965                 this.overlay.enableDisplayMode("block");
27966                 this.overlay.on("mousemove", this.onMouseMove, this);
27967                 this.overlay.on("mouseup", this.onMouseUp, this);
27968             }
27969             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27970
27971             this.resizing = true;
27972             this.startBox = this.el.getBox();
27973             this.startPoint = e.getXY();
27974             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27975                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27976
27977             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27978             this.overlay.show();
27979
27980             if(this.constrainTo) {
27981                 var ct = Roo.get(this.constrainTo);
27982                 this.resizeRegion = ct.getRegion().adjust(
27983                     ct.getFrameWidth('t'),
27984                     ct.getFrameWidth('l'),
27985                     -ct.getFrameWidth('b'),
27986                     -ct.getFrameWidth('r')
27987                 );
27988             }
27989
27990             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27991             this.proxy.show();
27992             this.proxy.setBox(this.startBox);
27993             if(!this.dynamic){
27994                 this.proxy.setStyle('visibility', 'visible');
27995             }
27996         }
27997     },
27998
27999     // private
28000     onMouseDown : function(handle, e){
28001         if(this.enabled){
28002             e.stopEvent();
28003             this.activeHandle = handle;
28004             this.startSizing(e, handle);
28005         }
28006     },
28007
28008     // private
28009     onMouseUp : function(e){
28010         var size = this.resizeElement();
28011         this.resizing = false;
28012         this.handleOut();
28013         this.overlay.hide();
28014         this.proxy.hide();
28015         this.fireEvent("resize", this, size.width, size.height, e);
28016     },
28017
28018     // private
28019     updateChildSize : function(){
28020         if(this.resizeChild){
28021             var el = this.el;
28022             var child = this.resizeChild;
28023             var adj = this.adjustments;
28024             if(el.dom.offsetWidth){
28025                 var b = el.getSize(true);
28026                 child.setSize(b.width+adj[0], b.height+adj[1]);
28027             }
28028             // Second call here for IE
28029             // The first call enables instant resizing and
28030             // the second call corrects scroll bars if they
28031             // exist
28032             if(Roo.isIE){
28033                 setTimeout(function(){
28034                     if(el.dom.offsetWidth){
28035                         var b = el.getSize(true);
28036                         child.setSize(b.width+adj[0], b.height+adj[1]);
28037                     }
28038                 }, 10);
28039             }
28040         }
28041     },
28042
28043     // private
28044     snap : function(value, inc, min){
28045         if(!inc || !value) return value;
28046         var newValue = value;
28047         var m = value % inc;
28048         if(m > 0){
28049             if(m > (inc/2)){
28050                 newValue = value + (inc-m);
28051             }else{
28052                 newValue = value - m;
28053             }
28054         }
28055         return Math.max(min, newValue);
28056     },
28057
28058     // private
28059     resizeElement : function(){
28060         var box = this.proxy.getBox();
28061         if(this.updateBox){
28062             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28063         }else{
28064             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28065         }
28066         this.updateChildSize();
28067         if(!this.dynamic){
28068             this.proxy.hide();
28069         }
28070         return box;
28071     },
28072
28073     // private
28074     constrain : function(v, diff, m, mx){
28075         if(v - diff < m){
28076             diff = v - m;
28077         }else if(v - diff > mx){
28078             diff = mx - v;
28079         }
28080         return diff;
28081     },
28082
28083     // private
28084     onMouseMove : function(e){
28085         if(this.enabled){
28086             try{// try catch so if something goes wrong the user doesn't get hung
28087
28088             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28089                 return;
28090             }
28091
28092             //var curXY = this.startPoint;
28093             var curSize = this.curSize || this.startBox;
28094             var x = this.startBox.x, y = this.startBox.y;
28095             var ox = x, oy = y;
28096             var w = curSize.width, h = curSize.height;
28097             var ow = w, oh = h;
28098             var mw = this.minWidth, mh = this.minHeight;
28099             var mxw = this.maxWidth, mxh = this.maxHeight;
28100             var wi = this.widthIncrement;
28101             var hi = this.heightIncrement;
28102
28103             var eventXY = e.getXY();
28104             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28105             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28106
28107             var pos = this.activeHandle.position;
28108
28109             switch(pos){
28110                 case "east":
28111                     w += diffX;
28112                     w = Math.min(Math.max(mw, w), mxw);
28113                     break;
28114              
28115                 case "south":
28116                     h += diffY;
28117                     h = Math.min(Math.max(mh, h), mxh);
28118                     break;
28119                 case "southeast":
28120                     w += diffX;
28121                     h += diffY;
28122                     w = Math.min(Math.max(mw, w), mxw);
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "north":
28126                     diffY = this.constrain(h, diffY, mh, mxh);
28127                     y += diffY;
28128                     h -= diffY;
28129                     break;
28130                 case "hdrag":
28131                     
28132                     if (wi) {
28133                         var adiffX = Math.abs(diffX);
28134                         var sub = (adiffX % wi); // how much 
28135                         if (sub > (wi/2)) { // far enough to snap
28136                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28137                         } else {
28138                             // remove difference.. 
28139                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28140                         }
28141                     }
28142                     x += diffX;
28143                     x = Math.max(this.minX, x);
28144                     break;
28145                 case "west":
28146                     diffX = this.constrain(w, diffX, mw, mxw);
28147                     x += diffX;
28148                     w -= diffX;
28149                     break;
28150                 case "northeast":
28151                     w += diffX;
28152                     w = Math.min(Math.max(mw, w), mxw);
28153                     diffY = this.constrain(h, diffY, mh, mxh);
28154                     y += diffY;
28155                     h -= diffY;
28156                     break;
28157                 case "northwest":
28158                     diffX = this.constrain(w, diffX, mw, mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     x += diffX;
28163                     w -= diffX;
28164                     break;
28165                case "southwest":
28166                     diffX = this.constrain(w, diffX, mw, mxw);
28167                     h += diffY;
28168                     h = Math.min(Math.max(mh, h), mxh);
28169                     x += diffX;
28170                     w -= diffX;
28171                     break;
28172             }
28173
28174             var sw = this.snap(w, wi, mw);
28175             var sh = this.snap(h, hi, mh);
28176             if(sw != w || sh != h){
28177                 switch(pos){
28178                     case "northeast":
28179                         y -= sh - h;
28180                     break;
28181                     case "north":
28182                         y -= sh - h;
28183                         break;
28184                     case "southwest":
28185                         x -= sw - w;
28186                     break;
28187                     case "west":
28188                         x -= sw - w;
28189                         break;
28190                     case "northwest":
28191                         x -= sw - w;
28192                         y -= sh - h;
28193                     break;
28194                 }
28195                 w = sw;
28196                 h = sh;
28197             }
28198
28199             if(this.preserveRatio){
28200                 switch(pos){
28201                     case "southeast":
28202                     case "east":
28203                         h = oh * (w/ow);
28204                         h = Math.min(Math.max(mh, h), mxh);
28205                         w = ow * (h/oh);
28206                        break;
28207                     case "south":
28208                         w = ow * (h/oh);
28209                         w = Math.min(Math.max(mw, w), mxw);
28210                         h = oh * (w/ow);
28211                         break;
28212                     case "northeast":
28213                         w = ow * (h/oh);
28214                         w = Math.min(Math.max(mw, w), mxw);
28215                         h = oh * (w/ow);
28216                     break;
28217                     case "north":
28218                         var tw = w;
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                         x += (tw - w) / 2;
28223                         break;
28224                     case "southwest":
28225                         h = oh * (w/ow);
28226                         h = Math.min(Math.max(mh, h), mxh);
28227                         var tw = w;
28228                         w = ow * (h/oh);
28229                         x += tw - w;
28230                         break;
28231                     case "west":
28232                         var th = h;
28233                         h = oh * (w/ow);
28234                         h = Math.min(Math.max(mh, h), mxh);
28235                         y += (th - h) / 2;
28236                         var tw = w;
28237                         w = ow * (h/oh);
28238                         x += tw - w;
28239                        break;
28240                     case "northwest":
28241                         var tw = w;
28242                         var th = h;
28243                         h = oh * (w/ow);
28244                         h = Math.min(Math.max(mh, h), mxh);
28245                         w = ow * (h/oh);
28246                         y += th - h;
28247                         x += tw - w;
28248                        break;
28249
28250                 }
28251             }
28252             if (pos == 'hdrag') {
28253                 w = ow;
28254             }
28255             this.proxy.setBounds(x, y, w, h);
28256             if(this.dynamic){
28257                 this.resizeElement();
28258             }
28259             }catch(e){}
28260         }
28261     },
28262
28263     // private
28264     handleOver : function(){
28265         if(this.enabled){
28266             this.el.addClass("x-resizable-over");
28267         }
28268     },
28269
28270     // private
28271     handleOut : function(){
28272         if(!this.resizing){
28273             this.el.removeClass("x-resizable-over");
28274         }
28275     },
28276
28277     /**
28278      * Returns the element this component is bound to.
28279      * @return {Roo.Element}
28280      */
28281     getEl : function(){
28282         return this.el;
28283     },
28284
28285     /**
28286      * Returns the resizeChild element (or null).
28287      * @return {Roo.Element}
28288      */
28289     getResizeChild : function(){
28290         return this.resizeChild;
28291     },
28292
28293     /**
28294      * Destroys this resizable. If the element was wrapped and
28295      * removeEl is not true then the element remains.
28296      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28297      */
28298     destroy : function(removeEl){
28299         this.proxy.remove();
28300         if(this.overlay){
28301             this.overlay.removeAllListeners();
28302             this.overlay.remove();
28303         }
28304         var ps = Roo.Resizable.positions;
28305         for(var k in ps){
28306             if(typeof ps[k] != "function" && this[ps[k]]){
28307                 var h = this[ps[k]];
28308                 h.el.removeAllListeners();
28309                 h.el.remove();
28310             }
28311         }
28312         if(removeEl){
28313             this.el.update("");
28314             this.el.remove();
28315         }
28316     }
28317 });
28318
28319 // private
28320 // hash to map config positions to true positions
28321 Roo.Resizable.positions = {
28322     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28323     hd: "hdrag"
28324 };
28325
28326 // private
28327 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28328     if(!this.tpl){
28329         // only initialize the template if resizable is used
28330         var tpl = Roo.DomHelper.createTemplate(
28331             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28332         );
28333         tpl.compile();
28334         Roo.Resizable.Handle.prototype.tpl = tpl;
28335     }
28336     this.position = pos;
28337     this.rz = rz;
28338     // show north drag fro topdra
28339     var handlepos = pos == 'hdrag' ? 'north' : pos;
28340     
28341     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28342     if (pos == 'hdrag') {
28343         this.el.setStyle('cursor', 'pointer');
28344     }
28345     this.el.unselectable();
28346     if(transparent){
28347         this.el.setOpacity(0);
28348     }
28349     this.el.on("mousedown", this.onMouseDown, this);
28350     if(!disableTrackOver){
28351         this.el.on("mouseover", this.onMouseOver, this);
28352         this.el.on("mouseout", this.onMouseOut, this);
28353     }
28354 };
28355
28356 // private
28357 Roo.Resizable.Handle.prototype = {
28358     afterResize : function(rz){
28359         // do nothing
28360     },
28361     // private
28362     onMouseDown : function(e){
28363         this.rz.onMouseDown(this, e);
28364     },
28365     // private
28366     onMouseOver : function(e){
28367         this.rz.handleOver(this, e);
28368     },
28369     // private
28370     onMouseOut : function(e){
28371         this.rz.handleOut(this, e);
28372     }
28373 };/*
28374  * Based on:
28375  * Ext JS Library 1.1.1
28376  * Copyright(c) 2006-2007, Ext JS, LLC.
28377  *
28378  * Originally Released Under LGPL - original licence link has changed is not relivant.
28379  *
28380  * Fork - LGPL
28381  * <script type="text/javascript">
28382  */
28383
28384 /**
28385  * @class Roo.Editor
28386  * @extends Roo.Component
28387  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28388  * @constructor
28389  * Create a new Editor
28390  * @param {Roo.form.Field} field The Field object (or descendant)
28391  * @param {Object} config The config object
28392  */
28393 Roo.Editor = function(field, config){
28394     Roo.Editor.superclass.constructor.call(this, config);
28395     this.field = field;
28396     this.addEvents({
28397         /**
28398              * @event beforestartedit
28399              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28400              * false from the handler of this event.
28401              * @param {Editor} this
28402              * @param {Roo.Element} boundEl The underlying element bound to this editor
28403              * @param {Mixed} value The field value being set
28404              */
28405         "beforestartedit" : true,
28406         /**
28407              * @event startedit
28408              * Fires when this editor is displayed
28409              * @param {Roo.Element} boundEl The underlying element bound to this editor
28410              * @param {Mixed} value The starting field value
28411              */
28412         "startedit" : true,
28413         /**
28414              * @event beforecomplete
28415              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28416              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28417              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28418              * event will not fire since no edit actually occurred.
28419              * @param {Editor} this
28420              * @param {Mixed} value The current field value
28421              * @param {Mixed} startValue The original field value
28422              */
28423         "beforecomplete" : true,
28424         /**
28425              * @event complete
28426              * Fires after editing is complete and any changed value has been written to the underlying field.
28427              * @param {Editor} this
28428              * @param {Mixed} value The current field value
28429              * @param {Mixed} startValue The original field value
28430              */
28431         "complete" : true,
28432         /**
28433          * @event specialkey
28434          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28435          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28436          * @param {Roo.form.Field} this
28437          * @param {Roo.EventObject} e The event object
28438          */
28439         "specialkey" : true
28440     });
28441 };
28442
28443 Roo.extend(Roo.Editor, Roo.Component, {
28444     /**
28445      * @cfg {Boolean/String} autosize
28446      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28447      * or "height" to adopt the height only (defaults to false)
28448      */
28449     /**
28450      * @cfg {Boolean} revertInvalid
28451      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28452      * validation fails (defaults to true)
28453      */
28454     /**
28455      * @cfg {Boolean} ignoreNoChange
28456      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28457      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28458      * will never be ignored.
28459      */
28460     /**
28461      * @cfg {Boolean} hideEl
28462      * False to keep the bound element visible while the editor is displayed (defaults to true)
28463      */
28464     /**
28465      * @cfg {Mixed} value
28466      * The data value of the underlying field (defaults to "")
28467      */
28468     value : "",
28469     /**
28470      * @cfg {String} alignment
28471      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28472      */
28473     alignment: "c-c?",
28474     /**
28475      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28476      * for bottom-right shadow (defaults to "frame")
28477      */
28478     shadow : "frame",
28479     /**
28480      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28481      */
28482     constrain : false,
28483     /**
28484      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28485      */
28486     completeOnEnter : false,
28487     /**
28488      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28489      */
28490     cancelOnEsc : false,
28491     /**
28492      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28493      */
28494     updateEl : false,
28495
28496     // private
28497     onRender : function(ct, position){
28498         this.el = new Roo.Layer({
28499             shadow: this.shadow,
28500             cls: "x-editor",
28501             parentEl : ct,
28502             shim : this.shim,
28503             shadowOffset:4,
28504             id: this.id,
28505             constrain: this.constrain
28506         });
28507         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28508         if(this.field.msgTarget != 'title'){
28509             this.field.msgTarget = 'qtip';
28510         }
28511         this.field.render(this.el);
28512         if(Roo.isGecko){
28513             this.field.el.dom.setAttribute('autocomplete', 'off');
28514         }
28515         this.field.on("specialkey", this.onSpecialKey, this);
28516         if(this.swallowKeys){
28517             this.field.el.swallowEvent(['keydown','keypress']);
28518         }
28519         this.field.show();
28520         this.field.on("blur", this.onBlur, this);
28521         if(this.field.grow){
28522             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28523         }
28524     },
28525
28526     onSpecialKey : function(field, e)
28527     {
28528         //Roo.log('editor onSpecialKey');
28529         if(this.completeOnEnter && e.getKey() == e.ENTER){
28530             e.stopEvent();
28531             this.completeEdit();
28532             return;
28533         }
28534         // do not fire special key otherwise it might hide close the editor...
28535         if(e.getKey() == e.ENTER){    
28536             return;
28537         }
28538         if(this.cancelOnEsc && e.getKey() == e.ESC){
28539             this.cancelEdit();
28540             return;
28541         } 
28542         this.fireEvent('specialkey', field, e);
28543     
28544     },
28545
28546     /**
28547      * Starts the editing process and shows the editor.
28548      * @param {String/HTMLElement/Element} el The element to edit
28549      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28550       * to the innerHTML of el.
28551      */
28552     startEdit : function(el, value){
28553         if(this.editing){
28554             this.completeEdit();
28555         }
28556         this.boundEl = Roo.get(el);
28557         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28558         if(!this.rendered){
28559             this.render(this.parentEl || document.body);
28560         }
28561         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28562             return;
28563         }
28564         this.startValue = v;
28565         this.field.setValue(v);
28566         if(this.autoSize){
28567             var sz = this.boundEl.getSize();
28568             switch(this.autoSize){
28569                 case "width":
28570                 this.setSize(sz.width,  "");
28571                 break;
28572                 case "height":
28573                 this.setSize("",  sz.height);
28574                 break;
28575                 default:
28576                 this.setSize(sz.width,  sz.height);
28577             }
28578         }
28579         this.el.alignTo(this.boundEl, this.alignment);
28580         this.editing = true;
28581         if(Roo.QuickTips){
28582             Roo.QuickTips.disable();
28583         }
28584         this.show();
28585     },
28586
28587     /**
28588      * Sets the height and width of this editor.
28589      * @param {Number} width The new width
28590      * @param {Number} height The new height
28591      */
28592     setSize : function(w, h){
28593         this.field.setSize(w, h);
28594         if(this.el){
28595             this.el.sync();
28596         }
28597     },
28598
28599     /**
28600      * Realigns the editor to the bound field based on the current alignment config value.
28601      */
28602     realign : function(){
28603         this.el.alignTo(this.boundEl, this.alignment);
28604     },
28605
28606     /**
28607      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28608      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28609      */
28610     completeEdit : function(remainVisible){
28611         if(!this.editing){
28612             return;
28613         }
28614         var v = this.getValue();
28615         if(this.revertInvalid !== false && !this.field.isValid()){
28616             v = this.startValue;
28617             this.cancelEdit(true);
28618         }
28619         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28620             this.editing = false;
28621             this.hide();
28622             return;
28623         }
28624         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28625             this.editing = false;
28626             if(this.updateEl && this.boundEl){
28627                 this.boundEl.update(v);
28628             }
28629             if(remainVisible !== true){
28630                 this.hide();
28631             }
28632             this.fireEvent("complete", this, v, this.startValue);
28633         }
28634     },
28635
28636     // private
28637     onShow : function(){
28638         this.el.show();
28639         if(this.hideEl !== false){
28640             this.boundEl.hide();
28641         }
28642         this.field.show();
28643         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28644             this.fixIEFocus = true;
28645             this.deferredFocus.defer(50, this);
28646         }else{
28647             this.field.focus();
28648         }
28649         this.fireEvent("startedit", this.boundEl, this.startValue);
28650     },
28651
28652     deferredFocus : function(){
28653         if(this.editing){
28654             this.field.focus();
28655         }
28656     },
28657
28658     /**
28659      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28660      * reverted to the original starting value.
28661      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28662      * cancel (defaults to false)
28663      */
28664     cancelEdit : function(remainVisible){
28665         if(this.editing){
28666             this.setValue(this.startValue);
28667             if(remainVisible !== true){
28668                 this.hide();
28669             }
28670         }
28671     },
28672
28673     // private
28674     onBlur : function(){
28675         if(this.allowBlur !== true && this.editing){
28676             this.completeEdit();
28677         }
28678     },
28679
28680     // private
28681     onHide : function(){
28682         if(this.editing){
28683             this.completeEdit();
28684             return;
28685         }
28686         this.field.blur();
28687         if(this.field.collapse){
28688             this.field.collapse();
28689         }
28690         this.el.hide();
28691         if(this.hideEl !== false){
28692             this.boundEl.show();
28693         }
28694         if(Roo.QuickTips){
28695             Roo.QuickTips.enable();
28696         }
28697     },
28698
28699     /**
28700      * Sets the data value of the editor
28701      * @param {Mixed} value Any valid value supported by the underlying field
28702      */
28703     setValue : function(v){
28704         this.field.setValue(v);
28705     },
28706
28707     /**
28708      * Gets the data value of the editor
28709      * @return {Mixed} The data value
28710      */
28711     getValue : function(){
28712         return this.field.getValue();
28713     }
28714 });/*
28715  * Based on:
28716  * Ext JS Library 1.1.1
28717  * Copyright(c) 2006-2007, Ext JS, LLC.
28718  *
28719  * Originally Released Under LGPL - original licence link has changed is not relivant.
28720  *
28721  * Fork - LGPL
28722  * <script type="text/javascript">
28723  */
28724  
28725 /**
28726  * @class Roo.BasicDialog
28727  * @extends Roo.util.Observable
28728  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28729  * <pre><code>
28730 var dlg = new Roo.BasicDialog("my-dlg", {
28731     height: 200,
28732     width: 300,
28733     minHeight: 100,
28734     minWidth: 150,
28735     modal: true,
28736     proxyDrag: true,
28737     shadow: true
28738 });
28739 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28740 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28741 dlg.addButton('Cancel', dlg.hide, dlg);
28742 dlg.show();
28743 </code></pre>
28744   <b>A Dialog should always be a direct child of the body element.</b>
28745  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28746  * @cfg {String} title Default text to display in the title bar (defaults to null)
28747  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28748  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28749  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28750  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28751  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28752  * (defaults to null with no animation)
28753  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28754  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28755  * property for valid values (defaults to 'all')
28756  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28757  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28758  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28759  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28760  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28761  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28762  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28763  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28764  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28765  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28766  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28767  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28768  * draggable = true (defaults to false)
28769  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28770  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28771  * shadow (defaults to false)
28772  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28773  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28774  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28775  * @cfg {Array} buttons Array of buttons
28776  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28777  * @constructor
28778  * Create a new BasicDialog.
28779  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28780  * @param {Object} config Configuration options
28781  */
28782 Roo.BasicDialog = function(el, config){
28783     this.el = Roo.get(el);
28784     var dh = Roo.DomHelper;
28785     if(!this.el && config && config.autoCreate){
28786         if(typeof config.autoCreate == "object"){
28787             if(!config.autoCreate.id){
28788                 config.autoCreate.id = el;
28789             }
28790             this.el = dh.append(document.body,
28791                         config.autoCreate, true);
28792         }else{
28793             this.el = dh.append(document.body,
28794                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28795         }
28796     }
28797     el = this.el;
28798     el.setDisplayed(true);
28799     el.hide = this.hideAction;
28800     this.id = el.id;
28801     el.addClass("x-dlg");
28802
28803     Roo.apply(this, config);
28804
28805     this.proxy = el.createProxy("x-dlg-proxy");
28806     this.proxy.hide = this.hideAction;
28807     this.proxy.setOpacity(.5);
28808     this.proxy.hide();
28809
28810     if(config.width){
28811         el.setWidth(config.width);
28812     }
28813     if(config.height){
28814         el.setHeight(config.height);
28815     }
28816     this.size = el.getSize();
28817     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28818         this.xy = [config.x,config.y];
28819     }else{
28820         this.xy = el.getCenterXY(true);
28821     }
28822     /** The header element @type Roo.Element */
28823     this.header = el.child("> .x-dlg-hd");
28824     /** The body element @type Roo.Element */
28825     this.body = el.child("> .x-dlg-bd");
28826     /** The footer element @type Roo.Element */
28827     this.footer = el.child("> .x-dlg-ft");
28828
28829     if(!this.header){
28830         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28831     }
28832     if(!this.body){
28833         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28834     }
28835
28836     this.header.unselectable();
28837     if(this.title){
28838         this.header.update(this.title);
28839     }
28840     // this element allows the dialog to be focused for keyboard event
28841     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28842     this.focusEl.swallowEvent("click", true);
28843
28844     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28845
28846     // wrap the body and footer for special rendering
28847     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28848     if(this.footer){
28849         this.bwrap.dom.appendChild(this.footer.dom);
28850     }
28851
28852     this.bg = this.el.createChild({
28853         tag: "div", cls:"x-dlg-bg",
28854         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28855     });
28856     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28857
28858
28859     if(this.autoScroll !== false && !this.autoTabs){
28860         this.body.setStyle("overflow", "auto");
28861     }
28862
28863     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28864
28865     if(this.closable !== false){
28866         this.el.addClass("x-dlg-closable");
28867         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28868         this.close.on("click", this.closeClick, this);
28869         this.close.addClassOnOver("x-dlg-close-over");
28870     }
28871     if(this.collapsible !== false){
28872         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28873         this.collapseBtn.on("click", this.collapseClick, this);
28874         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28875         this.header.on("dblclick", this.collapseClick, this);
28876     }
28877     if(this.resizable !== false){
28878         this.el.addClass("x-dlg-resizable");
28879         this.resizer = new Roo.Resizable(el, {
28880             minWidth: this.minWidth || 80,
28881             minHeight:this.minHeight || 80,
28882             handles: this.resizeHandles || "all",
28883             pinned: true
28884         });
28885         this.resizer.on("beforeresize", this.beforeResize, this);
28886         this.resizer.on("resize", this.onResize, this);
28887     }
28888     if(this.draggable !== false){
28889         el.addClass("x-dlg-draggable");
28890         if (!this.proxyDrag) {
28891             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28892         }
28893         else {
28894             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28895         }
28896         dd.setHandleElId(this.header.id);
28897         dd.endDrag = this.endMove.createDelegate(this);
28898         dd.startDrag = this.startMove.createDelegate(this);
28899         dd.onDrag = this.onDrag.createDelegate(this);
28900         dd.scroll = false;
28901         this.dd = dd;
28902     }
28903     if(this.modal){
28904         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28905         this.mask.enableDisplayMode("block");
28906         this.mask.hide();
28907         this.el.addClass("x-dlg-modal");
28908     }
28909     if(this.shadow){
28910         this.shadow = new Roo.Shadow({
28911             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28912             offset : this.shadowOffset
28913         });
28914     }else{
28915         this.shadowOffset = 0;
28916     }
28917     if(Roo.useShims && this.shim !== false){
28918         this.shim = this.el.createShim();
28919         this.shim.hide = this.hideAction;
28920         this.shim.hide();
28921     }else{
28922         this.shim = false;
28923     }
28924     if(this.autoTabs){
28925         this.initTabs();
28926     }
28927     if (this.buttons) { 
28928         var bts= this.buttons;
28929         this.buttons = [];
28930         Roo.each(bts, function(b) {
28931             this.addButton(b);
28932         }, this);
28933     }
28934     
28935     
28936     this.addEvents({
28937         /**
28938          * @event keydown
28939          * Fires when a key is pressed
28940          * @param {Roo.BasicDialog} this
28941          * @param {Roo.EventObject} e
28942          */
28943         "keydown" : true,
28944         /**
28945          * @event move
28946          * Fires when this dialog is moved by the user.
28947          * @param {Roo.BasicDialog} this
28948          * @param {Number} x The new page X
28949          * @param {Number} y The new page Y
28950          */
28951         "move" : true,
28952         /**
28953          * @event resize
28954          * Fires when this dialog is resized by the user.
28955          * @param {Roo.BasicDialog} this
28956          * @param {Number} width The new width
28957          * @param {Number} height The new height
28958          */
28959         "resize" : true,
28960         /**
28961          * @event beforehide
28962          * Fires before this dialog is hidden.
28963          * @param {Roo.BasicDialog} this
28964          */
28965         "beforehide" : true,
28966         /**
28967          * @event hide
28968          * Fires when this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "hide" : true,
28972         /**
28973          * @event beforeshow
28974          * Fires before this dialog is shown.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "beforeshow" : true,
28978         /**
28979          * @event show
28980          * Fires when this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "show" : true
28984     });
28985     el.on("keydown", this.onKeyDown, this);
28986     el.on("mousedown", this.toFront, this);
28987     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28988     this.el.hide();
28989     Roo.DialogManager.register(this);
28990     Roo.BasicDialog.superclass.constructor.call(this);
28991 };
28992
28993 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28994     shadowOffset: Roo.isIE ? 6 : 5,
28995     minHeight: 80,
28996     minWidth: 200,
28997     minButtonWidth: 75,
28998     defaultButton: null,
28999     buttonAlign: "right",
29000     tabTag: 'div',
29001     firstShow: true,
29002
29003     /**
29004      * Sets the dialog title text
29005      * @param {String} text The title text to display
29006      * @return {Roo.BasicDialog} this
29007      */
29008     setTitle : function(text){
29009         this.header.update(text);
29010         return this;
29011     },
29012
29013     // private
29014     closeClick : function(){
29015         this.hide();
29016     },
29017
29018     // private
29019     collapseClick : function(){
29020         this[this.collapsed ? "expand" : "collapse"]();
29021     },
29022
29023     /**
29024      * Collapses the dialog to its minimized state (only the title bar is visible).
29025      * Equivalent to the user clicking the collapse dialog button.
29026      */
29027     collapse : function(){
29028         if(!this.collapsed){
29029             this.collapsed = true;
29030             this.el.addClass("x-dlg-collapsed");
29031             this.restoreHeight = this.el.getHeight();
29032             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29033         }
29034     },
29035
29036     /**
29037      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29038      * clicking the expand dialog button.
29039      */
29040     expand : function(){
29041         if(this.collapsed){
29042             this.collapsed = false;
29043             this.el.removeClass("x-dlg-collapsed");
29044             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29045         }
29046     },
29047
29048     /**
29049      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29050      * @return {Roo.TabPanel} The tabs component
29051      */
29052     initTabs : function(){
29053         var tabs = this.getTabs();
29054         while(tabs.getTab(0)){
29055             tabs.removeTab(0);
29056         }
29057         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29058             var dom = el.dom;
29059             tabs.addTab(Roo.id(dom), dom.title);
29060             dom.title = "";
29061         });
29062         tabs.activate(0);
29063         return tabs;
29064     },
29065
29066     // private
29067     beforeResize : function(){
29068         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29069     },
29070
29071     // private
29072     onResize : function(){
29073         this.refreshSize();
29074         this.syncBodyHeight();
29075         this.adjustAssets();
29076         this.focus();
29077         this.fireEvent("resize", this, this.size.width, this.size.height);
29078     },
29079
29080     // private
29081     onKeyDown : function(e){
29082         if(this.isVisible()){
29083             this.fireEvent("keydown", this, e);
29084         }
29085     },
29086
29087     /**
29088      * Resizes the dialog.
29089      * @param {Number} width
29090      * @param {Number} height
29091      * @return {Roo.BasicDialog} this
29092      */
29093     resizeTo : function(width, height){
29094         this.el.setSize(width, height);
29095         this.size = {width: width, height: height};
29096         this.syncBodyHeight();
29097         if(this.fixedcenter){
29098             this.center();
29099         }
29100         if(this.isVisible()){
29101             this.constrainXY();
29102             this.adjustAssets();
29103         }
29104         this.fireEvent("resize", this, width, height);
29105         return this;
29106     },
29107
29108
29109     /**
29110      * Resizes the dialog to fit the specified content size.
29111      * @param {Number} width
29112      * @param {Number} height
29113      * @return {Roo.BasicDialog} this
29114      */
29115     setContentSize : function(w, h){
29116         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29117         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29118         //if(!this.el.isBorderBox()){
29119             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29120             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29121         //}
29122         if(this.tabs){
29123             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29124             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29125         }
29126         this.resizeTo(w, h);
29127         return this;
29128     },
29129
29130     /**
29131      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29132      * executed in response to a particular key being pressed while the dialog is active.
29133      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29134      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29135      * @param {Function} fn The function to call
29136      * @param {Object} scope (optional) The scope of the function
29137      * @return {Roo.BasicDialog} this
29138      */
29139     addKeyListener : function(key, fn, scope){
29140         var keyCode, shift, ctrl, alt;
29141         if(typeof key == "object" && !(key instanceof Array)){
29142             keyCode = key["key"];
29143             shift = key["shift"];
29144             ctrl = key["ctrl"];
29145             alt = key["alt"];
29146         }else{
29147             keyCode = key;
29148         }
29149         var handler = function(dlg, e){
29150             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29151                 var k = e.getKey();
29152                 if(keyCode instanceof Array){
29153                     for(var i = 0, len = keyCode.length; i < len; i++){
29154                         if(keyCode[i] == k){
29155                           fn.call(scope || window, dlg, k, e);
29156                           return;
29157                         }
29158                     }
29159                 }else{
29160                     if(k == keyCode){
29161                         fn.call(scope || window, dlg, k, e);
29162                     }
29163                 }
29164             }
29165         };
29166         this.on("keydown", handler);
29167         return this;
29168     },
29169
29170     /**
29171      * Returns the TabPanel component (creates it if it doesn't exist).
29172      * Note: If you wish to simply check for the existence of tabs without creating them,
29173      * check for a null 'tabs' property.
29174      * @return {Roo.TabPanel} The tabs component
29175      */
29176     getTabs : function(){
29177         if(!this.tabs){
29178             this.el.addClass("x-dlg-auto-tabs");
29179             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29180             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29181         }
29182         return this.tabs;
29183     },
29184
29185     /**
29186      * Adds a button to the footer section of the dialog.
29187      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29188      * object or a valid Roo.DomHelper element config
29189      * @param {Function} handler The function called when the button is clicked
29190      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29191      * @return {Roo.Button} The new button
29192      */
29193     addButton : function(config, handler, scope){
29194         var dh = Roo.DomHelper;
29195         if(!this.footer){
29196             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29197         }
29198         if(!this.btnContainer){
29199             var tb = this.footer.createChild({
29200
29201                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29202                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29203             }, null, true);
29204             this.btnContainer = tb.firstChild.firstChild.firstChild;
29205         }
29206         var bconfig = {
29207             handler: handler,
29208             scope: scope,
29209             minWidth: this.minButtonWidth,
29210             hideParent:true
29211         };
29212         if(typeof config == "string"){
29213             bconfig.text = config;
29214         }else{
29215             if(config.tag){
29216                 bconfig.dhconfig = config;
29217             }else{
29218                 Roo.apply(bconfig, config);
29219             }
29220         }
29221         var fc = false;
29222         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29223             bconfig.position = Math.max(0, bconfig.position);
29224             fc = this.btnContainer.childNodes[bconfig.position];
29225         }
29226          
29227         var btn = new Roo.Button(
29228             fc ? 
29229                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29230                 : this.btnContainer.appendChild(document.createElement("td")),
29231             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29232             bconfig
29233         );
29234         this.syncBodyHeight();
29235         if(!this.buttons){
29236             /**
29237              * Array of all the buttons that have been added to this dialog via addButton
29238              * @type Array
29239              */
29240             this.buttons = [];
29241         }
29242         this.buttons.push(btn);
29243         return btn;
29244     },
29245
29246     /**
29247      * Sets the default button to be focused when the dialog is displayed.
29248      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29249      * @return {Roo.BasicDialog} this
29250      */
29251     setDefaultButton : function(btn){
29252         this.defaultButton = btn;
29253         return this;
29254     },
29255
29256     // private
29257     getHeaderFooterHeight : function(safe){
29258         var height = 0;
29259         if(this.header){
29260            height += this.header.getHeight();
29261         }
29262         if(this.footer){
29263            var fm = this.footer.getMargins();
29264             height += (this.footer.getHeight()+fm.top+fm.bottom);
29265         }
29266         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29267         height += this.centerBg.getPadding("tb");
29268         return height;
29269     },
29270
29271     // private
29272     syncBodyHeight : function(){
29273         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29274         var height = this.size.height - this.getHeaderFooterHeight(false);
29275         bd.setHeight(height-bd.getMargins("tb"));
29276         var hh = this.header.getHeight();
29277         var h = this.size.height-hh;
29278         cb.setHeight(h);
29279         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29280         bw.setHeight(h-cb.getPadding("tb"));
29281         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29282         bd.setWidth(bw.getWidth(true));
29283         if(this.tabs){
29284             this.tabs.syncHeight();
29285             if(Roo.isIE){
29286                 this.tabs.el.repaint();
29287             }
29288         }
29289     },
29290
29291     /**
29292      * Restores the previous state of the dialog if Roo.state is configured.
29293      * @return {Roo.BasicDialog} this
29294      */
29295     restoreState : function(){
29296         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29297         if(box && box.width){
29298             this.xy = [box.x, box.y];
29299             this.resizeTo(box.width, box.height);
29300         }
29301         return this;
29302     },
29303
29304     // private
29305     beforeShow : function(){
29306         this.expand();
29307         if(this.fixedcenter){
29308             this.xy = this.el.getCenterXY(true);
29309         }
29310         if(this.modal){
29311             Roo.get(document.body).addClass("x-body-masked");
29312             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29313             this.mask.show();
29314         }
29315         this.constrainXY();
29316     },
29317
29318     // private
29319     animShow : function(){
29320         var b = Roo.get(this.animateTarget).getBox();
29321         this.proxy.setSize(b.width, b.height);
29322         this.proxy.setLocation(b.x, b.y);
29323         this.proxy.show();
29324         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29325                     true, .35, this.showEl.createDelegate(this));
29326     },
29327
29328     /**
29329      * Shows the dialog.
29330      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29331      * @return {Roo.BasicDialog} this
29332      */
29333     show : function(animateTarget){
29334         if (this.fireEvent("beforeshow", this) === false){
29335             return;
29336         }
29337         if(this.syncHeightBeforeShow){
29338             this.syncBodyHeight();
29339         }else if(this.firstShow){
29340             this.firstShow = false;
29341             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29342         }
29343         this.animateTarget = animateTarget || this.animateTarget;
29344         if(!this.el.isVisible()){
29345             this.beforeShow();
29346             if(this.animateTarget && Roo.get(this.animateTarget)){
29347                 this.animShow();
29348             }else{
29349                 this.showEl();
29350             }
29351         }
29352         return this;
29353     },
29354
29355     // private
29356     showEl : function(){
29357         this.proxy.hide();
29358         this.el.setXY(this.xy);
29359         this.el.show();
29360         this.adjustAssets(true);
29361         this.toFront();
29362         this.focus();
29363         // IE peekaboo bug - fix found by Dave Fenwick
29364         if(Roo.isIE){
29365             this.el.repaint();
29366         }
29367         this.fireEvent("show", this);
29368     },
29369
29370     /**
29371      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29372      * dialog itself will receive focus.
29373      */
29374     focus : function(){
29375         if(this.defaultButton){
29376             this.defaultButton.focus();
29377         }else{
29378             this.focusEl.focus();
29379         }
29380     },
29381
29382     // private
29383     constrainXY : function(){
29384         if(this.constraintoviewport !== false){
29385             if(!this.viewSize){
29386                 if(this.container){
29387                     var s = this.container.getSize();
29388                     this.viewSize = [s.width, s.height];
29389                 }else{
29390                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29391                 }
29392             }
29393             var s = Roo.get(this.container||document).getScroll();
29394
29395             var x = this.xy[0], y = this.xy[1];
29396             var w = this.size.width, h = this.size.height;
29397             var vw = this.viewSize[0], vh = this.viewSize[1];
29398             // only move it if it needs it
29399             var moved = false;
29400             // first validate right/bottom
29401             if(x + w > vw+s.left){
29402                 x = vw - w;
29403                 moved = true;
29404             }
29405             if(y + h > vh+s.top){
29406                 y = vh - h;
29407                 moved = true;
29408             }
29409             // then make sure top/left isn't negative
29410             if(x < s.left){
29411                 x = s.left;
29412                 moved = true;
29413             }
29414             if(y < s.top){
29415                 y = s.top;
29416                 moved = true;
29417             }
29418             if(moved){
29419                 // cache xy
29420                 this.xy = [x, y];
29421                 if(this.isVisible()){
29422                     this.el.setLocation(x, y);
29423                     this.adjustAssets();
29424                 }
29425             }
29426         }
29427     },
29428
29429     // private
29430     onDrag : function(){
29431         if(!this.proxyDrag){
29432             this.xy = this.el.getXY();
29433             this.adjustAssets();
29434         }
29435     },
29436
29437     // private
29438     adjustAssets : function(doShow){
29439         var x = this.xy[0], y = this.xy[1];
29440         var w = this.size.width, h = this.size.height;
29441         if(doShow === true){
29442             if(this.shadow){
29443                 this.shadow.show(this.el);
29444             }
29445             if(this.shim){
29446                 this.shim.show();
29447             }
29448         }
29449         if(this.shadow && this.shadow.isVisible()){
29450             this.shadow.show(this.el);
29451         }
29452         if(this.shim && this.shim.isVisible()){
29453             this.shim.setBounds(x, y, w, h);
29454         }
29455     },
29456
29457     // private
29458     adjustViewport : function(w, h){
29459         if(!w || !h){
29460             w = Roo.lib.Dom.getViewWidth();
29461             h = Roo.lib.Dom.getViewHeight();
29462         }
29463         // cache the size
29464         this.viewSize = [w, h];
29465         if(this.modal && this.mask.isVisible()){
29466             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29467             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29468         }
29469         if(this.isVisible()){
29470             this.constrainXY();
29471         }
29472     },
29473
29474     /**
29475      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29476      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29477      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29478      */
29479     destroy : function(removeEl){
29480         if(this.isVisible()){
29481             this.animateTarget = null;
29482             this.hide();
29483         }
29484         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29485         if(this.tabs){
29486             this.tabs.destroy(removeEl);
29487         }
29488         Roo.destroy(
29489              this.shim,
29490              this.proxy,
29491              this.resizer,
29492              this.close,
29493              this.mask
29494         );
29495         if(this.dd){
29496             this.dd.unreg();
29497         }
29498         if(this.buttons){
29499            for(var i = 0, len = this.buttons.length; i < len; i++){
29500                this.buttons[i].destroy();
29501            }
29502         }
29503         this.el.removeAllListeners();
29504         if(removeEl === true){
29505             this.el.update("");
29506             this.el.remove();
29507         }
29508         Roo.DialogManager.unregister(this);
29509     },
29510
29511     // private
29512     startMove : function(){
29513         if(this.proxyDrag){
29514             this.proxy.show();
29515         }
29516         if(this.constraintoviewport !== false){
29517             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29518         }
29519     },
29520
29521     // private
29522     endMove : function(){
29523         if(!this.proxyDrag){
29524             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29525         }else{
29526             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29527             this.proxy.hide();
29528         }
29529         this.refreshSize();
29530         this.adjustAssets();
29531         this.focus();
29532         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29533     },
29534
29535     /**
29536      * Brings this dialog to the front of any other visible dialogs
29537      * @return {Roo.BasicDialog} this
29538      */
29539     toFront : function(){
29540         Roo.DialogManager.bringToFront(this);
29541         return this;
29542     },
29543
29544     /**
29545      * Sends this dialog to the back (under) of any other visible dialogs
29546      * @return {Roo.BasicDialog} this
29547      */
29548     toBack : function(){
29549         Roo.DialogManager.sendToBack(this);
29550         return this;
29551     },
29552
29553     /**
29554      * Centers this dialog in the viewport
29555      * @return {Roo.BasicDialog} this
29556      */
29557     center : function(){
29558         var xy = this.el.getCenterXY(true);
29559         this.moveTo(xy[0], xy[1]);
29560         return this;
29561     },
29562
29563     /**
29564      * Moves the dialog's top-left corner to the specified point
29565      * @param {Number} x
29566      * @param {Number} y
29567      * @return {Roo.BasicDialog} this
29568      */
29569     moveTo : function(x, y){
29570         this.xy = [x,y];
29571         if(this.isVisible()){
29572             this.el.setXY(this.xy);
29573             this.adjustAssets();
29574         }
29575         return this;
29576     },
29577
29578     /**
29579      * Aligns the dialog to the specified element
29580      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29581      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29582      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29583      * @return {Roo.BasicDialog} this
29584      */
29585     alignTo : function(element, position, offsets){
29586         this.xy = this.el.getAlignToXY(element, position, offsets);
29587         if(this.isVisible()){
29588             this.el.setXY(this.xy);
29589             this.adjustAssets();
29590         }
29591         return this;
29592     },
29593
29594     /**
29595      * Anchors an element to another element and realigns it when the window is resized.
29596      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29597      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29598      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29599      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29600      * is a number, it is used as the buffer delay (defaults to 50ms).
29601      * @return {Roo.BasicDialog} this
29602      */
29603     anchorTo : function(el, alignment, offsets, monitorScroll){
29604         var action = function(){
29605             this.alignTo(el, alignment, offsets);
29606         };
29607         Roo.EventManager.onWindowResize(action, this);
29608         var tm = typeof monitorScroll;
29609         if(tm != 'undefined'){
29610             Roo.EventManager.on(window, 'scroll', action, this,
29611                 {buffer: tm == 'number' ? monitorScroll : 50});
29612         }
29613         action.call(this);
29614         return this;
29615     },
29616
29617     /**
29618      * Returns true if the dialog is visible
29619      * @return {Boolean}
29620      */
29621     isVisible : function(){
29622         return this.el.isVisible();
29623     },
29624
29625     // private
29626     animHide : function(callback){
29627         var b = Roo.get(this.animateTarget).getBox();
29628         this.proxy.show();
29629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29630         this.el.hide();
29631         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29632                     this.hideEl.createDelegate(this, [callback]));
29633     },
29634
29635     /**
29636      * Hides the dialog.
29637      * @param {Function} callback (optional) Function to call when the dialog is hidden
29638      * @return {Roo.BasicDialog} this
29639      */
29640     hide : function(callback){
29641         if (this.fireEvent("beforehide", this) === false){
29642             return;
29643         }
29644         if(this.shadow){
29645             this.shadow.hide();
29646         }
29647         if(this.shim) {
29648           this.shim.hide();
29649         }
29650         // sometimes animateTarget seems to get set.. causing problems...
29651         // this just double checks..
29652         if(this.animateTarget && Roo.get(this.animateTarget)) {
29653            this.animHide(callback);
29654         }else{
29655             this.el.hide();
29656             this.hideEl(callback);
29657         }
29658         return this;
29659     },
29660
29661     // private
29662     hideEl : function(callback){
29663         this.proxy.hide();
29664         if(this.modal){
29665             this.mask.hide();
29666             Roo.get(document.body).removeClass("x-body-masked");
29667         }
29668         this.fireEvent("hide", this);
29669         if(typeof callback == "function"){
29670             callback();
29671         }
29672     },
29673
29674     // private
29675     hideAction : function(){
29676         this.setLeft("-10000px");
29677         this.setTop("-10000px");
29678         this.setStyle("visibility", "hidden");
29679     },
29680
29681     // private
29682     refreshSize : function(){
29683         this.size = this.el.getSize();
29684         this.xy = this.el.getXY();
29685         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29686     },
29687
29688     // private
29689     // z-index is managed by the DialogManager and may be overwritten at any time
29690     setZIndex : function(index){
29691         if(this.modal){
29692             this.mask.setStyle("z-index", index);
29693         }
29694         if(this.shim){
29695             this.shim.setStyle("z-index", ++index);
29696         }
29697         if(this.shadow){
29698             this.shadow.setZIndex(++index);
29699         }
29700         this.el.setStyle("z-index", ++index);
29701         if(this.proxy){
29702             this.proxy.setStyle("z-index", ++index);
29703         }
29704         if(this.resizer){
29705             this.resizer.proxy.setStyle("z-index", ++index);
29706         }
29707
29708         this.lastZIndex = index;
29709     },
29710
29711     /**
29712      * Returns the element for this dialog
29713      * @return {Roo.Element} The underlying dialog Element
29714      */
29715     getEl : function(){
29716         return this.el;
29717     }
29718 });
29719
29720 /**
29721  * @class Roo.DialogManager
29722  * Provides global access to BasicDialogs that have been created and
29723  * support for z-indexing (layering) multiple open dialogs.
29724  */
29725 Roo.DialogManager = function(){
29726     var list = {};
29727     var accessList = [];
29728     var front = null;
29729
29730     // private
29731     var sortDialogs = function(d1, d2){
29732         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29733     };
29734
29735     // private
29736     var orderDialogs = function(){
29737         accessList.sort(sortDialogs);
29738         var seed = Roo.DialogManager.zseed;
29739         for(var i = 0, len = accessList.length; i < len; i++){
29740             var dlg = accessList[i];
29741             if(dlg){
29742                 dlg.setZIndex(seed + (i*10));
29743             }
29744         }
29745     };
29746
29747     return {
29748         /**
29749          * The starting z-index for BasicDialogs (defaults to 9000)
29750          * @type Number The z-index value
29751          */
29752         zseed : 9000,
29753
29754         // private
29755         register : function(dlg){
29756             list[dlg.id] = dlg;
29757             accessList.push(dlg);
29758         },
29759
29760         // private
29761         unregister : function(dlg){
29762             delete list[dlg.id];
29763             var i=0;
29764             var len=0;
29765             if(!accessList.indexOf){
29766                 for(  i = 0, len = accessList.length; i < len; i++){
29767                     if(accessList[i] == dlg){
29768                         accessList.splice(i, 1);
29769                         return;
29770                     }
29771                 }
29772             }else{
29773                  i = accessList.indexOf(dlg);
29774                 if(i != -1){
29775                     accessList.splice(i, 1);
29776                 }
29777             }
29778         },
29779
29780         /**
29781          * Gets a registered dialog by id
29782          * @param {String/Object} id The id of the dialog or a dialog
29783          * @return {Roo.BasicDialog} this
29784          */
29785         get : function(id){
29786             return typeof id == "object" ? id : list[id];
29787         },
29788
29789         /**
29790          * Brings the specified dialog to the front
29791          * @param {String/Object} dlg The id of the dialog or a dialog
29792          * @return {Roo.BasicDialog} this
29793          */
29794         bringToFront : function(dlg){
29795             dlg = this.get(dlg);
29796             if(dlg != front){
29797                 front = dlg;
29798                 dlg._lastAccess = new Date().getTime();
29799                 orderDialogs();
29800             }
29801             return dlg;
29802         },
29803
29804         /**
29805          * Sends the specified dialog to the back
29806          * @param {String/Object} dlg The id of the dialog or a dialog
29807          * @return {Roo.BasicDialog} this
29808          */
29809         sendToBack : function(dlg){
29810             dlg = this.get(dlg);
29811             dlg._lastAccess = -(new Date().getTime());
29812             orderDialogs();
29813             return dlg;
29814         },
29815
29816         /**
29817          * Hides all dialogs
29818          */
29819         hideAll : function(){
29820             for(var id in list){
29821                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29822                     list[id].hide();
29823                 }
29824             }
29825         }
29826     };
29827 }();
29828
29829 /**
29830  * @class Roo.LayoutDialog
29831  * @extends Roo.BasicDialog
29832  * Dialog which provides adjustments for working with a layout in a Dialog.
29833  * Add your necessary layout config options to the dialog's config.<br>
29834  * Example usage (including a nested layout):
29835  * <pre><code>
29836 if(!dialog){
29837     dialog = new Roo.LayoutDialog("download-dlg", {
29838         modal: true,
29839         width:600,
29840         height:450,
29841         shadow:true,
29842         minWidth:500,
29843         minHeight:350,
29844         autoTabs:true,
29845         proxyDrag:true,
29846         // layout config merges with the dialog config
29847         center:{
29848             tabPosition: "top",
29849             alwaysShowTabs: true
29850         }
29851     });
29852     dialog.addKeyListener(27, dialog.hide, dialog);
29853     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29854     dialog.addButton("Build It!", this.getDownload, this);
29855
29856     // we can even add nested layouts
29857     var innerLayout = new Roo.BorderLayout("dl-inner", {
29858         east: {
29859             initialSize: 200,
29860             autoScroll:true,
29861             split:true
29862         },
29863         center: {
29864             autoScroll:true
29865         }
29866     });
29867     innerLayout.beginUpdate();
29868     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29869     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29870     innerLayout.endUpdate(true);
29871
29872     var layout = dialog.getLayout();
29873     layout.beginUpdate();
29874     layout.add("center", new Roo.ContentPanel("standard-panel",
29875                         {title: "Download the Source", fitToFrame:true}));
29876     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29877                {title: "Build your own roo.js"}));
29878     layout.getRegion("center").showPanel(sp);
29879     layout.endUpdate();
29880 }
29881 </code></pre>
29882     * @constructor
29883     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29884     * @param {Object} config configuration options
29885   */
29886 Roo.LayoutDialog = function(el, cfg){
29887     
29888     var config=  cfg;
29889     if (typeof(cfg) == 'undefined') {
29890         config = Roo.apply({}, el);
29891         // not sure why we use documentElement here.. - it should always be body.
29892         // IE7 borks horribly if we use documentElement.
29893         // webkit also does not like documentElement - it creates a body element...
29894         el = Roo.get( document.body || document.documentElement ).createChild();
29895         //config.autoCreate = true;
29896     }
29897     
29898     
29899     config.autoTabs = false;
29900     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29901     this.body.setStyle({overflow:"hidden", position:"relative"});
29902     this.layout = new Roo.BorderLayout(this.body.dom, config);
29903     this.layout.monitorWindowResize = false;
29904     this.el.addClass("x-dlg-auto-layout");
29905     // fix case when center region overwrites center function
29906     this.center = Roo.BasicDialog.prototype.center;
29907     this.on("show", this.layout.layout, this.layout, true);
29908     if (config.items) {
29909         var xitems = config.items;
29910         delete config.items;
29911         Roo.each(xitems, this.addxtype, this);
29912     }
29913     
29914     
29915 };
29916 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29917     /**
29918      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29919      * @deprecated
29920      */
29921     endUpdate : function(){
29922         this.layout.endUpdate();
29923     },
29924
29925     /**
29926      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29927      *  @deprecated
29928      */
29929     beginUpdate : function(){
29930         this.layout.beginUpdate();
29931     },
29932
29933     /**
29934      * Get the BorderLayout for this dialog
29935      * @return {Roo.BorderLayout}
29936      */
29937     getLayout : function(){
29938         return this.layout;
29939     },
29940
29941     showEl : function(){
29942         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29943         if(Roo.isIE7){
29944             this.layout.layout();
29945         }
29946     },
29947
29948     // private
29949     // Use the syncHeightBeforeShow config option to control this automatically
29950     syncBodyHeight : function(){
29951         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29952         if(this.layout){this.layout.layout();}
29953     },
29954     
29955       /**
29956      * Add an xtype element (actually adds to the layout.)
29957      * @return {Object} xdata xtype object data.
29958      */
29959     
29960     addxtype : function(c) {
29961         return this.layout.addxtype(c);
29962     }
29963 });/*
29964  * Based on:
29965  * Ext JS Library 1.1.1
29966  * Copyright(c) 2006-2007, Ext JS, LLC.
29967  *
29968  * Originally Released Under LGPL - original licence link has changed is not relivant.
29969  *
29970  * Fork - LGPL
29971  * <script type="text/javascript">
29972  */
29973  
29974 /**
29975  * @class Roo.MessageBox
29976  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29977  * Example usage:
29978  *<pre><code>
29979 // Basic alert:
29980 Roo.Msg.alert('Status', 'Changes saved successfully.');
29981
29982 // Prompt for user data:
29983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29984     if (btn == 'ok'){
29985         // process text value...
29986     }
29987 });
29988
29989 // Show a dialog using config options:
29990 Roo.Msg.show({
29991    title:'Save Changes?',
29992    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29993    buttons: Roo.Msg.YESNOCANCEL,
29994    fn: processResult,
29995    animEl: 'elId'
29996 });
29997 </code></pre>
29998  * @singleton
29999  */
30000 Roo.MessageBox = function(){
30001     var dlg, opt, mask, waitTimer;
30002     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30003     var buttons, activeTextEl, bwidth;
30004
30005     // private
30006     var handleButton = function(button){
30007         dlg.hide();
30008         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30009     };
30010
30011     // private
30012     var handleHide = function(){
30013         if(opt && opt.cls){
30014             dlg.el.removeClass(opt.cls);
30015         }
30016         if(waitTimer){
30017             Roo.TaskMgr.stop(waitTimer);
30018             waitTimer = null;
30019         }
30020     };
30021
30022     // private
30023     var updateButtons = function(b){
30024         var width = 0;
30025         if(!b){
30026             buttons["ok"].hide();
30027             buttons["cancel"].hide();
30028             buttons["yes"].hide();
30029             buttons["no"].hide();
30030             dlg.footer.dom.style.display = 'none';
30031             return width;
30032         }
30033         dlg.footer.dom.style.display = '';
30034         for(var k in buttons){
30035             if(typeof buttons[k] != "function"){
30036                 if(b[k]){
30037                     buttons[k].show();
30038                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30039                     width += buttons[k].el.getWidth()+15;
30040                 }else{
30041                     buttons[k].hide();
30042                 }
30043             }
30044         }
30045         return width;
30046     };
30047
30048     // private
30049     var handleEsc = function(d, k, e){
30050         if(opt && opt.closable !== false){
30051             dlg.hide();
30052         }
30053         if(e){
30054             e.stopEvent();
30055         }
30056     };
30057
30058     return {
30059         /**
30060          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30061          * @return {Roo.BasicDialog} The BasicDialog element
30062          */
30063         getDialog : function(){
30064            if(!dlg){
30065                 dlg = new Roo.BasicDialog("x-msg-box", {
30066                     autoCreate : true,
30067                     shadow: true,
30068                     draggable: true,
30069                     resizable:false,
30070                     constraintoviewport:false,
30071                     fixedcenter:true,
30072                     collapsible : false,
30073                     shim:true,
30074                     modal: true,
30075                     width:400, height:100,
30076                     buttonAlign:"center",
30077                     closeClick : function(){
30078                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30079                             handleButton("no");
30080                         }else{
30081                             handleButton("cancel");
30082                         }
30083                     }
30084                 });
30085                 dlg.on("hide", handleHide);
30086                 mask = dlg.mask;
30087                 dlg.addKeyListener(27, handleEsc);
30088                 buttons = {};
30089                 var bt = this.buttonText;
30090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30094                 bodyEl = dlg.body.createChild({
30095
30096                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
30097                 });
30098                 msgEl = bodyEl.dom.firstChild;
30099                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30100                 textboxEl.enableDisplayMode();
30101                 textboxEl.addKeyListener([10,13], function(){
30102                     if(dlg.isVisible() && opt && opt.buttons){
30103                         if(opt.buttons.ok){
30104                             handleButton("ok");
30105                         }else if(opt.buttons.yes){
30106                             handleButton("yes");
30107                         }
30108                     }
30109                 });
30110                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30111                 textareaEl.enableDisplayMode();
30112                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30113                 progressEl.enableDisplayMode();
30114                 var pf = progressEl.dom.firstChild;
30115                 if (pf) {
30116                     pp = Roo.get(pf.firstChild);
30117                     pp.setHeight(pf.offsetHeight);
30118                 }
30119                 
30120             }
30121             return dlg;
30122         },
30123
30124         /**
30125          * Updates the message box body text
30126          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30127          * the XHTML-compliant non-breaking space character '&amp;#160;')
30128          * @return {Roo.MessageBox} This message box
30129          */
30130         updateText : function(text){
30131             if(!dlg.isVisible() && !opt.width){
30132                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30133             }
30134             msgEl.innerHTML = text || '&#160;';
30135             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30136                         Math.max(opt.minWidth || this.minWidth, bwidth));
30137             if(opt.prompt){
30138                 activeTextEl.setWidth(w);
30139             }
30140             if(dlg.isVisible()){
30141                 dlg.fixedcenter = false;
30142             }
30143             dlg.setContentSize(w, bodyEl.getHeight());
30144             if(dlg.isVisible()){
30145                 dlg.fixedcenter = true;
30146             }
30147             return this;
30148         },
30149
30150         /**
30151          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30152          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30153          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30154          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30155          * @return {Roo.MessageBox} This message box
30156          */
30157         updateProgress : function(value, text){
30158             if(text){
30159                 this.updateText(text);
30160             }
30161             if (pp) { // weird bug on my firefox - for some reason this is not defined
30162                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30163             }
30164             return this;
30165         },        
30166
30167         /**
30168          * Returns true if the message box is currently displayed
30169          * @return {Boolean} True if the message box is visible, else false
30170          */
30171         isVisible : function(){
30172             return dlg && dlg.isVisible();  
30173         },
30174
30175         /**
30176          * Hides the message box if it is displayed
30177          */
30178         hide : function(){
30179             if(this.isVisible()){
30180                 dlg.hide();
30181             }  
30182         },
30183
30184         /**
30185          * Displays a new message box, or reinitializes an existing message box, based on the config options
30186          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30187          * The following config object properties are supported:
30188          * <pre>
30189 Property    Type             Description
30190 ----------  ---------------  ------------------------------------------------------------------------------------
30191 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30192                                    closes (defaults to undefined)
30193 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30194                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30195 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30196                                    progress and wait dialogs will ignore this property and always hide the
30197                                    close button as they can only be closed programmatically.
30198 cls               String           A custom CSS class to apply to the message box element
30199 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30200                                    displayed (defaults to 75)
30201 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30202                                    function will be btn (the name of the button that was clicked, if applicable,
30203                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30204                                    Progress and wait dialogs will ignore this option since they do not respond to
30205                                    user actions and can only be closed programmatically, so any required function
30206                                    should be called by the same code after it closes the dialog.
30207 icon              String           A CSS class that provides a background image to be used as an icon for
30208                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30209 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30210 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30211 modal             Boolean          False to allow user interaction with the page while the message box is
30212                                    displayed (defaults to true)
30213 msg               String           A string that will replace the existing message box body text (defaults
30214                                    to the XHTML-compliant non-breaking space character '&#160;')
30215 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30216 progress          Boolean          True to display a progress bar (defaults to false)
30217 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30218 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30219 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30220 title             String           The title text
30221 value             String           The string value to set into the active textbox element if displayed
30222 wait              Boolean          True to display a progress bar (defaults to false)
30223 width             Number           The width of the dialog in pixels
30224 </pre>
30225          *
30226          * Example usage:
30227          * <pre><code>
30228 Roo.Msg.show({
30229    title: 'Address',
30230    msg: 'Please enter your address:',
30231    width: 300,
30232    buttons: Roo.MessageBox.OKCANCEL,
30233    multiline: true,
30234    fn: saveAddress,
30235    animEl: 'addAddressBtn'
30236 });
30237 </code></pre>
30238          * @param {Object} config Configuration options
30239          * @return {Roo.MessageBox} This message box
30240          */
30241         show : function(options)
30242         {
30243             
30244             // this causes nightmares if you show one dialog after another
30245             // especially on callbacks..
30246              
30247             if(this.isVisible()){
30248                 
30249                 this.hide();
30250                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30253                 
30254             }
30255             var d = this.getDialog();
30256             opt = options;
30257             d.setTitle(opt.title || "&#160;");
30258             d.close.setDisplayed(opt.closable !== false);
30259             activeTextEl = textboxEl;
30260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30261             if(opt.prompt){
30262                 if(opt.multiline){
30263                     textboxEl.hide();
30264                     textareaEl.show();
30265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30266                         opt.multiline : this.defaultTextHeight);
30267                     activeTextEl = textareaEl;
30268                 }else{
30269                     textboxEl.show();
30270                     textareaEl.hide();
30271                 }
30272             }else{
30273                 textboxEl.hide();
30274                 textareaEl.hide();
30275             }
30276             progressEl.setDisplayed(opt.progress === true);
30277             this.updateProgress(0);
30278             activeTextEl.dom.value = opt.value || "";
30279             if(opt.prompt){
30280                 dlg.setDefaultButton(activeTextEl);
30281             }else{
30282                 var bs = opt.buttons;
30283                 var db = null;
30284                 if(bs && bs.ok){
30285                     db = buttons["ok"];
30286                 }else if(bs && bs.yes){
30287                     db = buttons["yes"];
30288                 }
30289                 dlg.setDefaultButton(db);
30290             }
30291             bwidth = updateButtons(opt.buttons);
30292             this.updateText(opt.msg);
30293             if(opt.cls){
30294                 d.el.addClass(opt.cls);
30295             }
30296             d.proxyDrag = opt.proxyDrag === true;
30297             d.modal = opt.modal !== false;
30298             d.mask = opt.modal !== false ? mask : false;
30299             if(!d.isVisible()){
30300                 // force it to the end of the z-index stack so it gets a cursor in FF
30301                 document.body.appendChild(dlg.el.dom);
30302                 d.animateTarget = null;
30303                 d.show(options.animEl);
30304             }
30305             return this;
30306         },
30307
30308         /**
30309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30311          * and closing the message box when the process is complete.
30312          * @param {String} title The title bar text
30313          * @param {String} msg The message box body text
30314          * @return {Roo.MessageBox} This message box
30315          */
30316         progress : function(title, msg){
30317             this.show({
30318                 title : title,
30319                 msg : msg,
30320                 buttons: false,
30321                 progress:true,
30322                 closable:false,
30323                 minWidth: this.minProgressWidth,
30324                 modal : true
30325             });
30326             return this;
30327         },
30328
30329         /**
30330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30331          * If a callback function is passed it will be called after the user clicks the button, and the
30332          * id of the button that was clicked will be passed as the only parameter to the callback
30333          * (could also be the top-right close button).
30334          * @param {String} title The title bar text
30335          * @param {String} msg The message box body text
30336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30337          * @param {Object} scope (optional) The scope of the callback function
30338          * @return {Roo.MessageBox} This message box
30339          */
30340         alert : function(title, msg, fn, scope){
30341             this.show({
30342                 title : title,
30343                 msg : msg,
30344                 buttons: this.OK,
30345                 fn: fn,
30346                 scope : scope,
30347                 modal : true
30348             });
30349             return this;
30350         },
30351
30352         /**
30353          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30354          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30355          * You are responsible for closing the message box when the process is complete.
30356          * @param {String} msg The message box body text
30357          * @param {String} title (optional) The title bar text
30358          * @return {Roo.MessageBox} This message box
30359          */
30360         wait : function(msg, title){
30361             this.show({
30362                 title : title,
30363                 msg : msg,
30364                 buttons: false,
30365                 closable:false,
30366                 progress:true,
30367                 modal:true,
30368                 width:300,
30369                 wait:true
30370             });
30371             waitTimer = Roo.TaskMgr.start({
30372                 run: function(i){
30373                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30374                 },
30375                 interval: 1000
30376             });
30377             return this;
30378         },
30379
30380         /**
30381          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30382          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30383          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30384          * @param {String} title The title bar text
30385          * @param {String} msg The message box body text
30386          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30387          * @param {Object} scope (optional) The scope of the callback function
30388          * @return {Roo.MessageBox} This message box
30389          */
30390         confirm : function(title, msg, fn, scope){
30391             this.show({
30392                 title : title,
30393                 msg : msg,
30394                 buttons: this.YESNO,
30395                 fn: fn,
30396                 scope : scope,
30397                 modal : true
30398             });
30399             return this;
30400         },
30401
30402         /**
30403          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30404          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30405          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30406          * (could also be the top-right close button) and the text that was entered will be passed as the two
30407          * parameters to the callback.
30408          * @param {String} title The title bar text
30409          * @param {String} msg The message box body text
30410          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30411          * @param {Object} scope (optional) The scope of the callback function
30412          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30413          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30414          * @return {Roo.MessageBox} This message box
30415          */
30416         prompt : function(title, msg, fn, scope, multiline){
30417             this.show({
30418                 title : title,
30419                 msg : msg,
30420                 buttons: this.OKCANCEL,
30421                 fn: fn,
30422                 minWidth:250,
30423                 scope : scope,
30424                 prompt:true,
30425                 multiline: multiline,
30426                 modal : true
30427             });
30428             return this;
30429         },
30430
30431         /**
30432          * Button config that displays a single OK button
30433          * @type Object
30434          */
30435         OK : {ok:true},
30436         /**
30437          * Button config that displays Yes and No buttons
30438          * @type Object
30439          */
30440         YESNO : {yes:true, no:true},
30441         /**
30442          * Button config that displays OK and Cancel buttons
30443          * @type Object
30444          */
30445         OKCANCEL : {ok:true, cancel:true},
30446         /**
30447          * Button config that displays Yes, No and Cancel buttons
30448          * @type Object
30449          */
30450         YESNOCANCEL : {yes:true, no:true, cancel:true},
30451
30452         /**
30453          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30454          * @type Number
30455          */
30456         defaultTextHeight : 75,
30457         /**
30458          * The maximum width in pixels of the message box (defaults to 600)
30459          * @type Number
30460          */
30461         maxWidth : 600,
30462         /**
30463          * The minimum width in pixels of the message box (defaults to 100)
30464          * @type Number
30465          */
30466         minWidth : 100,
30467         /**
30468          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30469          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30470          * @type Number
30471          */
30472         minProgressWidth : 250,
30473         /**
30474          * An object containing the default button text strings that can be overriden for localized language support.
30475          * Supported properties are: ok, cancel, yes and no.
30476          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30477          * @type Object
30478          */
30479         buttonText : {
30480             ok : "OK",
30481             cancel : "Cancel",
30482             yes : "Yes",
30483             no : "No"
30484         }
30485     };
30486 }();
30487
30488 /**
30489  * Shorthand for {@link Roo.MessageBox}
30490  */
30491 Roo.Msg = Roo.MessageBox;/*
30492  * Based on:
30493  * Ext JS Library 1.1.1
30494  * Copyright(c) 2006-2007, Ext JS, LLC.
30495  *
30496  * Originally Released Under LGPL - original licence link has changed is not relivant.
30497  *
30498  * Fork - LGPL
30499  * <script type="text/javascript">
30500  */
30501 /**
30502  * @class Roo.QuickTips
30503  * Provides attractive and customizable tooltips for any element.
30504  * @singleton
30505  */
30506 Roo.QuickTips = function(){
30507     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30508     var ce, bd, xy, dd;
30509     var visible = false, disabled = true, inited = false;
30510     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30511     
30512     var onOver = function(e){
30513         if(disabled){
30514             return;
30515         }
30516         var t = e.getTarget();
30517         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30518             return;
30519         }
30520         if(ce && t == ce.el){
30521             clearTimeout(hideProc);
30522             return;
30523         }
30524         if(t && tagEls[t.id]){
30525             tagEls[t.id].el = t;
30526             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30527             return;
30528         }
30529         var ttp, et = Roo.fly(t);
30530         var ns = cfg.namespace;
30531         if(tm.interceptTitles && t.title){
30532             ttp = t.title;
30533             t.qtip = ttp;
30534             t.removeAttribute("title");
30535             e.preventDefault();
30536         }else{
30537             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30538         }
30539         if(ttp){
30540             showProc = show.defer(tm.showDelay, tm, [{
30541                 el: t, 
30542                 text: ttp, 
30543                 width: et.getAttributeNS(ns, cfg.width),
30544                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30545                 title: et.getAttributeNS(ns, cfg.title),
30546                     cls: et.getAttributeNS(ns, cfg.cls)
30547             }]);
30548         }
30549     };
30550     
30551     var onOut = function(e){
30552         clearTimeout(showProc);
30553         var t = e.getTarget();
30554         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30555             hideProc = setTimeout(hide, tm.hideDelay);
30556         }
30557     };
30558     
30559     var onMove = function(e){
30560         if(disabled){
30561             return;
30562         }
30563         xy = e.getXY();
30564         xy[1] += 18;
30565         if(tm.trackMouse && ce){
30566             el.setXY(xy);
30567         }
30568     };
30569     
30570     var onDown = function(e){
30571         clearTimeout(showProc);
30572         clearTimeout(hideProc);
30573         if(!e.within(el)){
30574             if(tm.hideOnClick){
30575                 hide();
30576                 tm.disable();
30577                 tm.enable.defer(100, tm);
30578             }
30579         }
30580     };
30581     
30582     var getPad = function(){
30583         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30584     };
30585
30586     var show = function(o){
30587         if(disabled){
30588             return;
30589         }
30590         clearTimeout(dismissProc);
30591         ce = o;
30592         if(removeCls){ // in case manually hidden
30593             el.removeClass(removeCls);
30594             removeCls = null;
30595         }
30596         if(ce.cls){
30597             el.addClass(ce.cls);
30598             removeCls = ce.cls;
30599         }
30600         if(ce.title){
30601             tipTitle.update(ce.title);
30602             tipTitle.show();
30603         }else{
30604             tipTitle.update('');
30605             tipTitle.hide();
30606         }
30607         el.dom.style.width  = tm.maxWidth+'px';
30608         //tipBody.dom.style.width = '';
30609         tipBodyText.update(o.text);
30610         var p = getPad(), w = ce.width;
30611         if(!w){
30612             var td = tipBodyText.dom;
30613             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30614             if(aw > tm.maxWidth){
30615                 w = tm.maxWidth;
30616             }else if(aw < tm.minWidth){
30617                 w = tm.minWidth;
30618             }else{
30619                 w = aw;
30620             }
30621         }
30622         //tipBody.setWidth(w);
30623         el.setWidth(parseInt(w, 10) + p);
30624         if(ce.autoHide === false){
30625             close.setDisplayed(true);
30626             if(dd){
30627                 dd.unlock();
30628             }
30629         }else{
30630             close.setDisplayed(false);
30631             if(dd){
30632                 dd.lock();
30633             }
30634         }
30635         if(xy){
30636             el.avoidY = xy[1]-18;
30637             el.setXY(xy);
30638         }
30639         if(tm.animate){
30640             el.setOpacity(.1);
30641             el.setStyle("visibility", "visible");
30642             el.fadeIn({callback: afterShow});
30643         }else{
30644             afterShow();
30645         }
30646     };
30647     
30648     var afterShow = function(){
30649         if(ce){
30650             el.show();
30651             esc.enable();
30652             if(tm.autoDismiss && ce.autoHide !== false){
30653                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30654             }
30655         }
30656     };
30657     
30658     var hide = function(noanim){
30659         clearTimeout(dismissProc);
30660         clearTimeout(hideProc);
30661         ce = null;
30662         if(el.isVisible()){
30663             esc.disable();
30664             if(noanim !== true && tm.animate){
30665                 el.fadeOut({callback: afterHide});
30666             }else{
30667                 afterHide();
30668             } 
30669         }
30670     };
30671     
30672     var afterHide = function(){
30673         el.hide();
30674         if(removeCls){
30675             el.removeClass(removeCls);
30676             removeCls = null;
30677         }
30678     };
30679     
30680     return {
30681         /**
30682         * @cfg {Number} minWidth
30683         * The minimum width of the quick tip (defaults to 40)
30684         */
30685        minWidth : 40,
30686         /**
30687         * @cfg {Number} maxWidth
30688         * The maximum width of the quick tip (defaults to 300)
30689         */
30690        maxWidth : 300,
30691         /**
30692         * @cfg {Boolean} interceptTitles
30693         * True to automatically use the element's DOM title value if available (defaults to false)
30694         */
30695        interceptTitles : false,
30696         /**
30697         * @cfg {Boolean} trackMouse
30698         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30699         */
30700        trackMouse : false,
30701         /**
30702         * @cfg {Boolean} hideOnClick
30703         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30704         */
30705        hideOnClick : true,
30706         /**
30707         * @cfg {Number} showDelay
30708         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30709         */
30710        showDelay : 500,
30711         /**
30712         * @cfg {Number} hideDelay
30713         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30714         */
30715        hideDelay : 200,
30716         /**
30717         * @cfg {Boolean} autoHide
30718         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30719         * Used in conjunction with hideDelay.
30720         */
30721        autoHide : true,
30722         /**
30723         * @cfg {Boolean}
30724         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30725         * (defaults to true).  Used in conjunction with autoDismissDelay.
30726         */
30727        autoDismiss : true,
30728         /**
30729         * @cfg {Number}
30730         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30731         */
30732        autoDismissDelay : 5000,
30733        /**
30734         * @cfg {Boolean} animate
30735         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30736         */
30737        animate : false,
30738
30739        /**
30740         * @cfg {String} title
30741         * Title text to display (defaults to '').  This can be any valid HTML markup.
30742         */
30743         title: '',
30744        /**
30745         * @cfg {String} text
30746         * Body text to display (defaults to '').  This can be any valid HTML markup.
30747         */
30748         text : '',
30749        /**
30750         * @cfg {String} cls
30751         * A CSS class to apply to the base quick tip element (defaults to '').
30752         */
30753         cls : '',
30754        /**
30755         * @cfg {Number} width
30756         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30757         * minWidth or maxWidth.
30758         */
30759         width : null,
30760
30761     /**
30762      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30763      * or display QuickTips in a page.
30764      */
30765        init : function(){
30766           tm = Roo.QuickTips;
30767           cfg = tm.tagConfig;
30768           if(!inited){
30769               if(!Roo.isReady){ // allow calling of init() before onReady
30770                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30771                   return;
30772               }
30773               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30774               el.fxDefaults = {stopFx: true};
30775               // maximum custom styling
30776               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30777               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30778               tipTitle = el.child('h3');
30779               tipTitle.enableDisplayMode("block");
30780               tipBody = el.child('div.x-tip-bd');
30781               tipBodyText = el.child('div.x-tip-bd-inner');
30782               //bdLeft = el.child('div.x-tip-bd-left');
30783               //bdRight = el.child('div.x-tip-bd-right');
30784               close = el.child('div.x-tip-close');
30785               close.enableDisplayMode("block");
30786               close.on("click", hide);
30787               var d = Roo.get(document);
30788               d.on("mousedown", onDown);
30789               d.on("mouseover", onOver);
30790               d.on("mouseout", onOut);
30791               d.on("mousemove", onMove);
30792               esc = d.addKeyListener(27, hide);
30793               esc.disable();
30794               if(Roo.dd.DD){
30795                   dd = el.initDD("default", null, {
30796                       onDrag : function(){
30797                           el.sync();  
30798                       }
30799                   });
30800                   dd.setHandleElId(tipTitle.id);
30801                   dd.lock();
30802               }
30803               inited = true;
30804           }
30805           this.enable(); 
30806        },
30807
30808     /**
30809      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30810      * are supported:
30811      * <pre>
30812 Property    Type                   Description
30813 ----------  ---------------------  ------------------------------------------------------------------------
30814 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30815      * </ul>
30816      * @param {Object} config The config object
30817      */
30818        register : function(config){
30819            var cs = config instanceof Array ? config : arguments;
30820            for(var i = 0, len = cs.length; i < len; i++) {
30821                var c = cs[i];
30822                var target = c.target;
30823                if(target){
30824                    if(target instanceof Array){
30825                        for(var j = 0, jlen = target.length; j < jlen; j++){
30826                            tagEls[target[j]] = c;
30827                        }
30828                    }else{
30829                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30830                    }
30831                }
30832            }
30833        },
30834
30835     /**
30836      * Removes this quick tip from its element and destroys it.
30837      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30838      */
30839        unregister : function(el){
30840            delete tagEls[Roo.id(el)];
30841        },
30842
30843     /**
30844      * Enable this quick tip.
30845      */
30846        enable : function(){
30847            if(inited && disabled){
30848                locks.pop();
30849                if(locks.length < 1){
30850                    disabled = false;
30851                }
30852            }
30853        },
30854
30855     /**
30856      * Disable this quick tip.
30857      */
30858        disable : function(){
30859           disabled = true;
30860           clearTimeout(showProc);
30861           clearTimeout(hideProc);
30862           clearTimeout(dismissProc);
30863           if(ce){
30864               hide(true);
30865           }
30866           locks.push(1);
30867        },
30868
30869     /**
30870      * Returns true if the quick tip is enabled, else false.
30871      */
30872        isEnabled : function(){
30873             return !disabled;
30874        },
30875
30876         // private
30877        tagConfig : {
30878            namespace : "ext",
30879            attribute : "qtip",
30880            width : "width",
30881            target : "target",
30882            title : "qtitle",
30883            hide : "hide",
30884            cls : "qclass"
30885        }
30886    };
30887 }();
30888
30889 // backwards compat
30890 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30891  * Based on:
30892  * Ext JS Library 1.1.1
30893  * Copyright(c) 2006-2007, Ext JS, LLC.
30894  *
30895  * Originally Released Under LGPL - original licence link has changed is not relivant.
30896  *
30897  * Fork - LGPL
30898  * <script type="text/javascript">
30899  */
30900  
30901
30902 /**
30903  * @class Roo.tree.TreePanel
30904  * @extends Roo.data.Tree
30905
30906  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30907  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30908  * @cfg {Boolean} enableDD true to enable drag and drop
30909  * @cfg {Boolean} enableDrag true to enable just drag
30910  * @cfg {Boolean} enableDrop true to enable just drop
30911  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30912  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30913  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30914  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30915  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30916  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30917  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30918  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30919  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30920  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30921  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30922  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30923  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30924  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30925  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30926  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30927  * 
30928  * @constructor
30929  * @param {String/HTMLElement/Element} el The container element
30930  * @param {Object} config
30931  */
30932 Roo.tree.TreePanel = function(el, config){
30933     var root = false;
30934     var loader = false;
30935     if (config.root) {
30936         root = config.root;
30937         delete config.root;
30938     }
30939     if (config.loader) {
30940         loader = config.loader;
30941         delete config.loader;
30942     }
30943     
30944     Roo.apply(this, config);
30945     Roo.tree.TreePanel.superclass.constructor.call(this);
30946     this.el = Roo.get(el);
30947     this.el.addClass('x-tree');
30948     //console.log(root);
30949     if (root) {
30950         this.setRootNode( Roo.factory(root, Roo.tree));
30951     }
30952     if (loader) {
30953         this.loader = Roo.factory(loader, Roo.tree);
30954     }
30955    /**
30956     * Read-only. The id of the container element becomes this TreePanel's id.
30957     */
30958     this.id = this.el.id;
30959     this.addEvents({
30960         /**
30961         * @event beforeload
30962         * Fires before a node is loaded, return false to cancel
30963         * @param {Node} node The node being loaded
30964         */
30965         "beforeload" : true,
30966         /**
30967         * @event load
30968         * Fires when a node is loaded
30969         * @param {Node} node The node that was loaded
30970         */
30971         "load" : true,
30972         /**
30973         * @event textchange
30974         * Fires when the text for a node is changed
30975         * @param {Node} node The node
30976         * @param {String} text The new text
30977         * @param {String} oldText The old text
30978         */
30979         "textchange" : true,
30980         /**
30981         * @event beforeexpand
30982         * Fires before a node is expanded, return false to cancel.
30983         * @param {Node} node The node
30984         * @param {Boolean} deep
30985         * @param {Boolean} anim
30986         */
30987         "beforeexpand" : true,
30988         /**
30989         * @event beforecollapse
30990         * Fires before a node is collapsed, return false to cancel.
30991         * @param {Node} node The node
30992         * @param {Boolean} deep
30993         * @param {Boolean} anim
30994         */
30995         "beforecollapse" : true,
30996         /**
30997         * @event expand
30998         * Fires when a node is expanded
30999         * @param {Node} node The node
31000         */
31001         "expand" : true,
31002         /**
31003         * @event disabledchange
31004         * Fires when the disabled status of a node changes
31005         * @param {Node} node The node
31006         * @param {Boolean} disabled
31007         */
31008         "disabledchange" : true,
31009         /**
31010         * @event collapse
31011         * Fires when a node is collapsed
31012         * @param {Node} node The node
31013         */
31014         "collapse" : true,
31015         /**
31016         * @event beforeclick
31017         * Fires before click processing on a node. Return false to cancel the default action.
31018         * @param {Node} node The node
31019         * @param {Roo.EventObject} e The event object
31020         */
31021         "beforeclick":true,
31022         /**
31023         * @event checkchange
31024         * Fires when a node with a checkbox's checked property changes
31025         * @param {Node} this This node
31026         * @param {Boolean} checked
31027         */
31028         "checkchange":true,
31029         /**
31030         * @event click
31031         * Fires when a node is clicked
31032         * @param {Node} node The node
31033         * @param {Roo.EventObject} e The event object
31034         */
31035         "click":true,
31036         /**
31037         * @event dblclick
31038         * Fires when a node is double clicked
31039         * @param {Node} node The node
31040         * @param {Roo.EventObject} e The event object
31041         */
31042         "dblclick":true,
31043         /**
31044         * @event contextmenu
31045         * Fires when a node is right clicked
31046         * @param {Node} node The node
31047         * @param {Roo.EventObject} e The event object
31048         */
31049         "contextmenu":true,
31050         /**
31051         * @event beforechildrenrendered
31052         * Fires right before the child nodes for a node are rendered
31053         * @param {Node} node The node
31054         */
31055         "beforechildrenrendered":true,
31056         /**
31057         * @event startdrag
31058         * Fires when a node starts being dragged
31059         * @param {Roo.tree.TreePanel} this
31060         * @param {Roo.tree.TreeNode} node
31061         * @param {event} e The raw browser event
31062         */ 
31063        "startdrag" : true,
31064        /**
31065         * @event enddrag
31066         * Fires when a drag operation is complete
31067         * @param {Roo.tree.TreePanel} this
31068         * @param {Roo.tree.TreeNode} node
31069         * @param {event} e The raw browser event
31070         */
31071        "enddrag" : true,
31072        /**
31073         * @event dragdrop
31074         * Fires when a dragged node is dropped on a valid DD target
31075         * @param {Roo.tree.TreePanel} this
31076         * @param {Roo.tree.TreeNode} node
31077         * @param {DD} dd The dd it was dropped on
31078         * @param {event} e The raw browser event
31079         */
31080        "dragdrop" : true,
31081        /**
31082         * @event beforenodedrop
31083         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31084         * passed to handlers has the following properties:<br />
31085         * <ul style="padding:5px;padding-left:16px;">
31086         * <li>tree - The TreePanel</li>
31087         * <li>target - The node being targeted for the drop</li>
31088         * <li>data - The drag data from the drag source</li>
31089         * <li>point - The point of the drop - append, above or below</li>
31090         * <li>source - The drag source</li>
31091         * <li>rawEvent - Raw mouse event</li>
31092         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31093         * to be inserted by setting them on this object.</li>
31094         * <li>cancel - Set this to true to cancel the drop.</li>
31095         * </ul>
31096         * @param {Object} dropEvent
31097         */
31098        "beforenodedrop" : true,
31099        /**
31100         * @event nodedrop
31101         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31102         * passed to handlers has the following properties:<br />
31103         * <ul style="padding:5px;padding-left:16px;">
31104         * <li>tree - The TreePanel</li>
31105         * <li>target - The node being targeted for the drop</li>
31106         * <li>data - The drag data from the drag source</li>
31107         * <li>point - The point of the drop - append, above or below</li>
31108         * <li>source - The drag source</li>
31109         * <li>rawEvent - Raw mouse event</li>
31110         * <li>dropNode - Dropped node(s).</li>
31111         * </ul>
31112         * @param {Object} dropEvent
31113         */
31114        "nodedrop" : true,
31115         /**
31116         * @event nodedragover
31117         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31118         * passed to handlers has the following properties:<br />
31119         * <ul style="padding:5px;padding-left:16px;">
31120         * <li>tree - The TreePanel</li>
31121         * <li>target - The node being targeted for the drop</li>
31122         * <li>data - The drag data from the drag source</li>
31123         * <li>point - The point of the drop - append, above or below</li>
31124         * <li>source - The drag source</li>
31125         * <li>rawEvent - Raw mouse event</li>
31126         * <li>dropNode - Drop node(s) provided by the source.</li>
31127         * <li>cancel - Set this to true to signal drop not allowed.</li>
31128         * </ul>
31129         * @param {Object} dragOverEvent
31130         */
31131        "nodedragover" : true
31132         
31133     });
31134     if(this.singleExpand){
31135        this.on("beforeexpand", this.restrictExpand, this);
31136     }
31137     if (this.editor) {
31138         this.editor.tree = this;
31139         this.editor = Roo.factory(this.editor, Roo.tree);
31140     }
31141     
31142     if (this.selModel) {
31143         this.selModel = Roo.factory(this.selModel, Roo.tree);
31144     }
31145    
31146 };
31147 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31148     rootVisible : true,
31149     animate: Roo.enableFx,
31150     lines : true,
31151     enableDD : false,
31152     hlDrop : Roo.enableFx,
31153   
31154     renderer: false,
31155     
31156     rendererTip: false,
31157     // private
31158     restrictExpand : function(node){
31159         var p = node.parentNode;
31160         if(p){
31161             if(p.expandedChild && p.expandedChild.parentNode == p){
31162                 p.expandedChild.collapse();
31163             }
31164             p.expandedChild = node;
31165         }
31166     },
31167
31168     // private override
31169     setRootNode : function(node){
31170         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31171         if(!this.rootVisible){
31172             node.ui = new Roo.tree.RootTreeNodeUI(node);
31173         }
31174         return node;
31175     },
31176
31177     /**
31178      * Returns the container element for this TreePanel
31179      */
31180     getEl : function(){
31181         return this.el;
31182     },
31183
31184     /**
31185      * Returns the default TreeLoader for this TreePanel
31186      */
31187     getLoader : function(){
31188         return this.loader;
31189     },
31190
31191     /**
31192      * Expand all nodes
31193      */
31194     expandAll : function(){
31195         this.root.expand(true);
31196     },
31197
31198     /**
31199      * Collapse all nodes
31200      */
31201     collapseAll : function(){
31202         this.root.collapse(true);
31203     },
31204
31205     /**
31206      * Returns the selection model used by this TreePanel
31207      */
31208     getSelectionModel : function(){
31209         if(!this.selModel){
31210             this.selModel = new Roo.tree.DefaultSelectionModel();
31211         }
31212         return this.selModel;
31213     },
31214
31215     /**
31216      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31217      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31218      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31219      * @return {Array}
31220      */
31221     getChecked : function(a, startNode){
31222         startNode = startNode || this.root;
31223         var r = [];
31224         var f = function(){
31225             if(this.attributes.checked){
31226                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31227             }
31228         }
31229         startNode.cascade(f);
31230         return r;
31231     },
31232
31233     /**
31234      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31235      * @param {String} path
31236      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31237      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31238      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31239      */
31240     expandPath : function(path, attr, callback){
31241         attr = attr || "id";
31242         var keys = path.split(this.pathSeparator);
31243         var curNode = this.root;
31244         if(curNode.attributes[attr] != keys[1]){ // invalid root
31245             if(callback){
31246                 callback(false, null);
31247             }
31248             return;
31249         }
31250         var index = 1;
31251         var f = function(){
31252             if(++index == keys.length){
31253                 if(callback){
31254                     callback(true, curNode);
31255                 }
31256                 return;
31257             }
31258             var c = curNode.findChild(attr, keys[index]);
31259             if(!c){
31260                 if(callback){
31261                     callback(false, curNode);
31262                 }
31263                 return;
31264             }
31265             curNode = c;
31266             c.expand(false, false, f);
31267         };
31268         curNode.expand(false, false, f);
31269     },
31270
31271     /**
31272      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31273      * @param {String} path
31274      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31275      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31276      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31277      */
31278     selectPath : function(path, attr, callback){
31279         attr = attr || "id";
31280         var keys = path.split(this.pathSeparator);
31281         var v = keys.pop();
31282         if(keys.length > 0){
31283             var f = function(success, node){
31284                 if(success && node){
31285                     var n = node.findChild(attr, v);
31286                     if(n){
31287                         n.select();
31288                         if(callback){
31289                             callback(true, n);
31290                         }
31291                     }else if(callback){
31292                         callback(false, n);
31293                     }
31294                 }else{
31295                     if(callback){
31296                         callback(false, n);
31297                     }
31298                 }
31299             };
31300             this.expandPath(keys.join(this.pathSeparator), attr, f);
31301         }else{
31302             this.root.select();
31303             if(callback){
31304                 callback(true, this.root);
31305             }
31306         }
31307     },
31308
31309     getTreeEl : function(){
31310         return this.el;
31311     },
31312
31313     /**
31314      * Trigger rendering of this TreePanel
31315      */
31316     render : function(){
31317         if (this.innerCt) {
31318             return this; // stop it rendering more than once!!
31319         }
31320         
31321         this.innerCt = this.el.createChild({tag:"ul",
31322                cls:"x-tree-root-ct " +
31323                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31324
31325         if(this.containerScroll){
31326             Roo.dd.ScrollManager.register(this.el);
31327         }
31328         if((this.enableDD || this.enableDrop) && !this.dropZone){
31329            /**
31330             * The dropZone used by this tree if drop is enabled
31331             * @type Roo.tree.TreeDropZone
31332             */
31333              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31334                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31335            });
31336         }
31337         if((this.enableDD || this.enableDrag) && !this.dragZone){
31338            /**
31339             * The dragZone used by this tree if drag is enabled
31340             * @type Roo.tree.TreeDragZone
31341             */
31342             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31343                ddGroup: this.ddGroup || "TreeDD",
31344                scroll: this.ddScroll
31345            });
31346         }
31347         this.getSelectionModel().init(this);
31348         if (!this.root) {
31349             console.log("ROOT not set in tree");
31350             return;
31351         }
31352         this.root.render();
31353         if(!this.rootVisible){
31354             this.root.renderChildren();
31355         }
31356         return this;
31357     }
31358 });/*
31359  * Based on:
31360  * Ext JS Library 1.1.1
31361  * Copyright(c) 2006-2007, Ext JS, LLC.
31362  *
31363  * Originally Released Under LGPL - original licence link has changed is not relivant.
31364  *
31365  * Fork - LGPL
31366  * <script type="text/javascript">
31367  */
31368  
31369
31370 /**
31371  * @class Roo.tree.DefaultSelectionModel
31372  * @extends Roo.util.Observable
31373  * The default single selection for a TreePanel.
31374  * @param {Object} cfg Configuration
31375  */
31376 Roo.tree.DefaultSelectionModel = function(cfg){
31377    this.selNode = null;
31378    
31379    
31380    
31381    this.addEvents({
31382        /**
31383         * @event selectionchange
31384         * Fires when the selected node changes
31385         * @param {DefaultSelectionModel} this
31386         * @param {TreeNode} node the new selection
31387         */
31388        "selectionchange" : true,
31389
31390        /**
31391         * @event beforeselect
31392         * Fires before the selected node changes, return false to cancel the change
31393         * @param {DefaultSelectionModel} this
31394         * @param {TreeNode} node the new selection
31395         * @param {TreeNode} node the old selection
31396         */
31397        "beforeselect" : true
31398    });
31399    
31400     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31401 };
31402
31403 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31404     init : function(tree){
31405         this.tree = tree;
31406         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31407         tree.on("click", this.onNodeClick, this);
31408     },
31409     
31410     onNodeClick : function(node, e){
31411         if (e.ctrlKey && this.selNode == node)  {
31412             this.unselect(node);
31413             return;
31414         }
31415         this.select(node);
31416     },
31417     
31418     /**
31419      * Select a node.
31420      * @param {TreeNode} node The node to select
31421      * @return {TreeNode} The selected node
31422      */
31423     select : function(node){
31424         var last = this.selNode;
31425         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31426             if(last){
31427                 last.ui.onSelectedChange(false);
31428             }
31429             this.selNode = node;
31430             node.ui.onSelectedChange(true);
31431             this.fireEvent("selectionchange", this, node, last);
31432         }
31433         return node;
31434     },
31435     
31436     /**
31437      * Deselect a node.
31438      * @param {TreeNode} node The node to unselect
31439      */
31440     unselect : function(node){
31441         if(this.selNode == node){
31442             this.clearSelections();
31443         }    
31444     },
31445     
31446     /**
31447      * Clear all selections
31448      */
31449     clearSelections : function(){
31450         var n = this.selNode;
31451         if(n){
31452             n.ui.onSelectedChange(false);
31453             this.selNode = null;
31454             this.fireEvent("selectionchange", this, null);
31455         }
31456         return n;
31457     },
31458     
31459     /**
31460      * Get the selected node
31461      * @return {TreeNode} The selected node
31462      */
31463     getSelectedNode : function(){
31464         return this.selNode;    
31465     },
31466     
31467     /**
31468      * Returns true if the node is selected
31469      * @param {TreeNode} node The node to check
31470      * @return {Boolean}
31471      */
31472     isSelected : function(node){
31473         return this.selNode == node;  
31474     },
31475
31476     /**
31477      * Selects the node above the selected node in the tree, intelligently walking the nodes
31478      * @return TreeNode The new selection
31479      */
31480     selectPrevious : function(){
31481         var s = this.selNode || this.lastSelNode;
31482         if(!s){
31483             return null;
31484         }
31485         var ps = s.previousSibling;
31486         if(ps){
31487             if(!ps.isExpanded() || ps.childNodes.length < 1){
31488                 return this.select(ps);
31489             } else{
31490                 var lc = ps.lastChild;
31491                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31492                     lc = lc.lastChild;
31493                 }
31494                 return this.select(lc);
31495             }
31496         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31497             return this.select(s.parentNode);
31498         }
31499         return null;
31500     },
31501
31502     /**
31503      * Selects the node above the selected node in the tree, intelligently walking the nodes
31504      * @return TreeNode The new selection
31505      */
31506     selectNext : function(){
31507         var s = this.selNode || this.lastSelNode;
31508         if(!s){
31509             return null;
31510         }
31511         if(s.firstChild && s.isExpanded()){
31512              return this.select(s.firstChild);
31513          }else if(s.nextSibling){
31514              return this.select(s.nextSibling);
31515          }else if(s.parentNode){
31516             var newS = null;
31517             s.parentNode.bubble(function(){
31518                 if(this.nextSibling){
31519                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31520                     return false;
31521                 }
31522             });
31523             return newS;
31524          }
31525         return null;
31526     },
31527
31528     onKeyDown : function(e){
31529         var s = this.selNode || this.lastSelNode;
31530         // undesirable, but required
31531         var sm = this;
31532         if(!s){
31533             return;
31534         }
31535         var k = e.getKey();
31536         switch(k){
31537              case e.DOWN:
31538                  e.stopEvent();
31539                  this.selectNext();
31540              break;
31541              case e.UP:
31542                  e.stopEvent();
31543                  this.selectPrevious();
31544              break;
31545              case e.RIGHT:
31546                  e.preventDefault();
31547                  if(s.hasChildNodes()){
31548                      if(!s.isExpanded()){
31549                          s.expand();
31550                      }else if(s.firstChild){
31551                          this.select(s.firstChild, e);
31552                      }
31553                  }
31554              break;
31555              case e.LEFT:
31556                  e.preventDefault();
31557                  if(s.hasChildNodes() && s.isExpanded()){
31558                      s.collapse();
31559                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31560                      this.select(s.parentNode, e);
31561                  }
31562              break;
31563         };
31564     }
31565 });
31566
31567 /**
31568  * @class Roo.tree.MultiSelectionModel
31569  * @extends Roo.util.Observable
31570  * Multi selection for a TreePanel.
31571  * @param {Object} cfg Configuration
31572  */
31573 Roo.tree.MultiSelectionModel = function(){
31574    this.selNodes = [];
31575    this.selMap = {};
31576    this.addEvents({
31577        /**
31578         * @event selectionchange
31579         * Fires when the selected nodes change
31580         * @param {MultiSelectionModel} this
31581         * @param {Array} nodes Array of the selected nodes
31582         */
31583        "selectionchange" : true
31584    });
31585    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31586    
31587 };
31588
31589 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31590     init : function(tree){
31591         this.tree = tree;
31592         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31593         tree.on("click", this.onNodeClick, this);
31594     },
31595     
31596     onNodeClick : function(node, e){
31597         this.select(node, e, e.ctrlKey);
31598     },
31599     
31600     /**
31601      * Select a node.
31602      * @param {TreeNode} node The node to select
31603      * @param {EventObject} e (optional) An event associated with the selection
31604      * @param {Boolean} keepExisting True to retain existing selections
31605      * @return {TreeNode} The selected node
31606      */
31607     select : function(node, e, keepExisting){
31608         if(keepExisting !== true){
31609             this.clearSelections(true);
31610         }
31611         if(this.isSelected(node)){
31612             this.lastSelNode = node;
31613             return node;
31614         }
31615         this.selNodes.push(node);
31616         this.selMap[node.id] = node;
31617         this.lastSelNode = node;
31618         node.ui.onSelectedChange(true);
31619         this.fireEvent("selectionchange", this, this.selNodes);
31620         return node;
31621     },
31622     
31623     /**
31624      * Deselect a node.
31625      * @param {TreeNode} node The node to unselect
31626      */
31627     unselect : function(node){
31628         if(this.selMap[node.id]){
31629             node.ui.onSelectedChange(false);
31630             var sn = this.selNodes;
31631             var index = -1;
31632             if(sn.indexOf){
31633                 index = sn.indexOf(node);
31634             }else{
31635                 for(var i = 0, len = sn.length; i < len; i++){
31636                     if(sn[i] == node){
31637                         index = i;
31638                         break;
31639                     }
31640                 }
31641             }
31642             if(index != -1){
31643                 this.selNodes.splice(index, 1);
31644             }
31645             delete this.selMap[node.id];
31646             this.fireEvent("selectionchange", this, this.selNodes);
31647         }
31648     },
31649     
31650     /**
31651      * Clear all selections
31652      */
31653     clearSelections : function(suppressEvent){
31654         var sn = this.selNodes;
31655         if(sn.length > 0){
31656             for(var i = 0, len = sn.length; i < len; i++){
31657                 sn[i].ui.onSelectedChange(false);
31658             }
31659             this.selNodes = [];
31660             this.selMap = {};
31661             if(suppressEvent !== true){
31662                 this.fireEvent("selectionchange", this, this.selNodes);
31663             }
31664         }
31665     },
31666     
31667     /**
31668      * Returns true if the node is selected
31669      * @param {TreeNode} node The node to check
31670      * @return {Boolean}
31671      */
31672     isSelected : function(node){
31673         return this.selMap[node.id] ? true : false;  
31674     },
31675     
31676     /**
31677      * Returns an array of the selected nodes
31678      * @return {Array}
31679      */
31680     getSelectedNodes : function(){
31681         return this.selNodes;    
31682     },
31683
31684     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31685
31686     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31687
31688     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31689 });/*
31690  * Based on:
31691  * Ext JS Library 1.1.1
31692  * Copyright(c) 2006-2007, Ext JS, LLC.
31693  *
31694  * Originally Released Under LGPL - original licence link has changed is not relivant.
31695  *
31696  * Fork - LGPL
31697  * <script type="text/javascript">
31698  */
31699  
31700 /**
31701  * @class Roo.tree.TreeNode
31702  * @extends Roo.data.Node
31703  * @cfg {String} text The text for this node
31704  * @cfg {Boolean} expanded true to start the node expanded
31705  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31706  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31707  * @cfg {Boolean} disabled true to start the node disabled
31708  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31709  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31710  * @cfg {String} cls A css class to be added to the node
31711  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31712  * @cfg {String} href URL of the link used for the node (defaults to #)
31713  * @cfg {String} hrefTarget target frame for the link
31714  * @cfg {String} qtip An Ext QuickTip for the node
31715  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31716  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31717  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31718  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31719  * (defaults to undefined with no checkbox rendered)
31720  * @constructor
31721  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31722  */
31723 Roo.tree.TreeNode = function(attributes){
31724     attributes = attributes || {};
31725     if(typeof attributes == "string"){
31726         attributes = {text: attributes};
31727     }
31728     this.childrenRendered = false;
31729     this.rendered = false;
31730     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31731     this.expanded = attributes.expanded === true;
31732     this.isTarget = attributes.isTarget !== false;
31733     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31734     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31735
31736     /**
31737      * Read-only. The text for this node. To change it use setText().
31738      * @type String
31739      */
31740     this.text = attributes.text;
31741     /**
31742      * True if this node is disabled.
31743      * @type Boolean
31744      */
31745     this.disabled = attributes.disabled === true;
31746
31747     this.addEvents({
31748         /**
31749         * @event textchange
31750         * Fires when the text for this node is changed
31751         * @param {Node} this This node
31752         * @param {String} text The new text
31753         * @param {String} oldText The old text
31754         */
31755         "textchange" : true,
31756         /**
31757         * @event beforeexpand
31758         * Fires before this node is expanded, return false to cancel.
31759         * @param {Node} this This node
31760         * @param {Boolean} deep
31761         * @param {Boolean} anim
31762         */
31763         "beforeexpand" : true,
31764         /**
31765         * @event beforecollapse
31766         * Fires before this node is collapsed, return false to cancel.
31767         * @param {Node} this This node
31768         * @param {Boolean} deep
31769         * @param {Boolean} anim
31770         */
31771         "beforecollapse" : true,
31772         /**
31773         * @event expand
31774         * Fires when this node is expanded
31775         * @param {Node} this This node
31776         */
31777         "expand" : true,
31778         /**
31779         * @event disabledchange
31780         * Fires when the disabled status of this node changes
31781         * @param {Node} this This node
31782         * @param {Boolean} disabled
31783         */
31784         "disabledchange" : true,
31785         /**
31786         * @event collapse
31787         * Fires when this node is collapsed
31788         * @param {Node} this This node
31789         */
31790         "collapse" : true,
31791         /**
31792         * @event beforeclick
31793         * Fires before click processing. Return false to cancel the default action.
31794         * @param {Node} this This node
31795         * @param {Roo.EventObject} e The event object
31796         */
31797         "beforeclick":true,
31798         /**
31799         * @event checkchange
31800         * Fires when a node with a checkbox's checked property changes
31801         * @param {Node} this This node
31802         * @param {Boolean} checked
31803         */
31804         "checkchange":true,
31805         /**
31806         * @event click
31807         * Fires when this node is clicked
31808         * @param {Node} this This node
31809         * @param {Roo.EventObject} e The event object
31810         */
31811         "click":true,
31812         /**
31813         * @event dblclick
31814         * Fires when this node is double clicked
31815         * @param {Node} this This node
31816         * @param {Roo.EventObject} e The event object
31817         */
31818         "dblclick":true,
31819         /**
31820         * @event contextmenu
31821         * Fires when this node is right clicked
31822         * @param {Node} this This node
31823         * @param {Roo.EventObject} e The event object
31824         */
31825         "contextmenu":true,
31826         /**
31827         * @event beforechildrenrendered
31828         * Fires right before the child nodes for this node are rendered
31829         * @param {Node} this This node
31830         */
31831         "beforechildrenrendered":true
31832     });
31833
31834     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31835
31836     /**
31837      * Read-only. The UI for this node
31838      * @type TreeNodeUI
31839      */
31840     this.ui = new uiClass(this);
31841 };
31842 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31843     preventHScroll: true,
31844     /**
31845      * Returns true if this node is expanded
31846      * @return {Boolean}
31847      */
31848     isExpanded : function(){
31849         return this.expanded;
31850     },
31851
31852     /**
31853      * Returns the UI object for this node
31854      * @return {TreeNodeUI}
31855      */
31856     getUI : function(){
31857         return this.ui;
31858     },
31859
31860     // private override
31861     setFirstChild : function(node){
31862         var of = this.firstChild;
31863         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31864         if(this.childrenRendered && of && node != of){
31865             of.renderIndent(true, true);
31866         }
31867         if(this.rendered){
31868             this.renderIndent(true, true);
31869         }
31870     },
31871
31872     // private override
31873     setLastChild : function(node){
31874         var ol = this.lastChild;
31875         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31876         if(this.childrenRendered && ol && node != ol){
31877             ol.renderIndent(true, true);
31878         }
31879         if(this.rendered){
31880             this.renderIndent(true, true);
31881         }
31882     },
31883
31884     // these methods are overridden to provide lazy rendering support
31885     // private override
31886     appendChild : function(){
31887         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31888         if(node && this.childrenRendered){
31889             node.render();
31890         }
31891         this.ui.updateExpandIcon();
31892         return node;
31893     },
31894
31895     // private override
31896     removeChild : function(node){
31897         this.ownerTree.getSelectionModel().unselect(node);
31898         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31899         // if it's been rendered remove dom node
31900         if(this.childrenRendered){
31901             node.ui.remove();
31902         }
31903         if(this.childNodes.length < 1){
31904             this.collapse(false, false);
31905         }else{
31906             this.ui.updateExpandIcon();
31907         }
31908         if(!this.firstChild) {
31909             this.childrenRendered = false;
31910         }
31911         return node;
31912     },
31913
31914     // private override
31915     insertBefore : function(node, refNode){
31916         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31917         if(newNode && refNode && this.childrenRendered){
31918             node.render();
31919         }
31920         this.ui.updateExpandIcon();
31921         return newNode;
31922     },
31923
31924     /**
31925      * Sets the text for this node
31926      * @param {String} text
31927      */
31928     setText : function(text){
31929         var oldText = this.text;
31930         this.text = text;
31931         this.attributes.text = text;
31932         if(this.rendered){ // event without subscribing
31933             this.ui.onTextChange(this, text, oldText);
31934         }
31935         this.fireEvent("textchange", this, text, oldText);
31936     },
31937
31938     /**
31939      * Triggers selection of this node
31940      */
31941     select : function(){
31942         this.getOwnerTree().getSelectionModel().select(this);
31943     },
31944
31945     /**
31946      * Triggers deselection of this node
31947      */
31948     unselect : function(){
31949         this.getOwnerTree().getSelectionModel().unselect(this);
31950     },
31951
31952     /**
31953      * Returns true if this node is selected
31954      * @return {Boolean}
31955      */
31956     isSelected : function(){
31957         return this.getOwnerTree().getSelectionModel().isSelected(this);
31958     },
31959
31960     /**
31961      * Expand this node.
31962      * @param {Boolean} deep (optional) True to expand all children as well
31963      * @param {Boolean} anim (optional) false to cancel the default animation
31964      * @param {Function} callback (optional) A callback to be called when
31965      * expanding this node completes (does not wait for deep expand to complete).
31966      * Called with 1 parameter, this node.
31967      */
31968     expand : function(deep, anim, callback){
31969         if(!this.expanded){
31970             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31971                 return;
31972             }
31973             if(!this.childrenRendered){
31974                 this.renderChildren();
31975             }
31976             this.expanded = true;
31977             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31978                 this.ui.animExpand(function(){
31979                     this.fireEvent("expand", this);
31980                     if(typeof callback == "function"){
31981                         callback(this);
31982                     }
31983                     if(deep === true){
31984                         this.expandChildNodes(true);
31985                     }
31986                 }.createDelegate(this));
31987                 return;
31988             }else{
31989                 this.ui.expand();
31990                 this.fireEvent("expand", this);
31991                 if(typeof callback == "function"){
31992                     callback(this);
31993                 }
31994             }
31995         }else{
31996            if(typeof callback == "function"){
31997                callback(this);
31998            }
31999         }
32000         if(deep === true){
32001             this.expandChildNodes(true);
32002         }
32003     },
32004
32005     isHiddenRoot : function(){
32006         return this.isRoot && !this.getOwnerTree().rootVisible;
32007     },
32008
32009     /**
32010      * Collapse this node.
32011      * @param {Boolean} deep (optional) True to collapse all children as well
32012      * @param {Boolean} anim (optional) false to cancel the default animation
32013      */
32014     collapse : function(deep, anim){
32015         if(this.expanded && !this.isHiddenRoot()){
32016             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32017                 return;
32018             }
32019             this.expanded = false;
32020             if((this.getOwnerTree().animate && anim !== false) || anim){
32021                 this.ui.animCollapse(function(){
32022                     this.fireEvent("collapse", this);
32023                     if(deep === true){
32024                         this.collapseChildNodes(true);
32025                     }
32026                 }.createDelegate(this));
32027                 return;
32028             }else{
32029                 this.ui.collapse();
32030                 this.fireEvent("collapse", this);
32031             }
32032         }
32033         if(deep === true){
32034             var cs = this.childNodes;
32035             for(var i = 0, len = cs.length; i < len; i++) {
32036                 cs[i].collapse(true, false);
32037             }
32038         }
32039     },
32040
32041     // private
32042     delayedExpand : function(delay){
32043         if(!this.expandProcId){
32044             this.expandProcId = this.expand.defer(delay, this);
32045         }
32046     },
32047
32048     // private
32049     cancelExpand : function(){
32050         if(this.expandProcId){
32051             clearTimeout(this.expandProcId);
32052         }
32053         this.expandProcId = false;
32054     },
32055
32056     /**
32057      * Toggles expanded/collapsed state of the node
32058      */
32059     toggle : function(){
32060         if(this.expanded){
32061             this.collapse();
32062         }else{
32063             this.expand();
32064         }
32065     },
32066
32067     /**
32068      * Ensures all parent nodes are expanded
32069      */
32070     ensureVisible : function(callback){
32071         var tree = this.getOwnerTree();
32072         tree.expandPath(this.parentNode.getPath(), false, function(){
32073             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32074             Roo.callback(callback);
32075         }.createDelegate(this));
32076     },
32077
32078     /**
32079      * Expand all child nodes
32080      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32081      */
32082     expandChildNodes : function(deep){
32083         var cs = this.childNodes;
32084         for(var i = 0, len = cs.length; i < len; i++) {
32085                 cs[i].expand(deep);
32086         }
32087     },
32088
32089     /**
32090      * Collapse all child nodes
32091      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32092      */
32093     collapseChildNodes : function(deep){
32094         var cs = this.childNodes;
32095         for(var i = 0, len = cs.length; i < len; i++) {
32096                 cs[i].collapse(deep);
32097         }
32098     },
32099
32100     /**
32101      * Disables this node
32102      */
32103     disable : function(){
32104         this.disabled = true;
32105         this.unselect();
32106         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32107             this.ui.onDisableChange(this, true);
32108         }
32109         this.fireEvent("disabledchange", this, true);
32110     },
32111
32112     /**
32113      * Enables this node
32114      */
32115     enable : function(){
32116         this.disabled = false;
32117         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32118             this.ui.onDisableChange(this, false);
32119         }
32120         this.fireEvent("disabledchange", this, false);
32121     },
32122
32123     // private
32124     renderChildren : function(suppressEvent){
32125         if(suppressEvent !== false){
32126             this.fireEvent("beforechildrenrendered", this);
32127         }
32128         var cs = this.childNodes;
32129         for(var i = 0, len = cs.length; i < len; i++){
32130             cs[i].render(true);
32131         }
32132         this.childrenRendered = true;
32133     },
32134
32135     // private
32136     sort : function(fn, scope){
32137         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32138         if(this.childrenRendered){
32139             var cs = this.childNodes;
32140             for(var i = 0, len = cs.length; i < len; i++){
32141                 cs[i].render(true);
32142             }
32143         }
32144     },
32145
32146     // private
32147     render : function(bulkRender){
32148         this.ui.render(bulkRender);
32149         if(!this.rendered){
32150             this.rendered = true;
32151             if(this.expanded){
32152                 this.expanded = false;
32153                 this.expand(false, false);
32154             }
32155         }
32156     },
32157
32158     // private
32159     renderIndent : function(deep, refresh){
32160         if(refresh){
32161             this.ui.childIndent = null;
32162         }
32163         this.ui.renderIndent();
32164         if(deep === true && this.childrenRendered){
32165             var cs = this.childNodes;
32166             for(var i = 0, len = cs.length; i < len; i++){
32167                 cs[i].renderIndent(true, refresh);
32168             }
32169         }
32170     }
32171 });/*
32172  * Based on:
32173  * Ext JS Library 1.1.1
32174  * Copyright(c) 2006-2007, Ext JS, LLC.
32175  *
32176  * Originally Released Under LGPL - original licence link has changed is not relivant.
32177  *
32178  * Fork - LGPL
32179  * <script type="text/javascript">
32180  */
32181  
32182 /**
32183  * @class Roo.tree.AsyncTreeNode
32184  * @extends Roo.tree.TreeNode
32185  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32186  * @constructor
32187  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32188  */
32189  Roo.tree.AsyncTreeNode = function(config){
32190     this.loaded = false;
32191     this.loading = false;
32192     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32193     /**
32194     * @event beforeload
32195     * Fires before this node is loaded, return false to cancel
32196     * @param {Node} this This node
32197     */
32198     this.addEvents({'beforeload':true, 'load': true});
32199     /**
32200     * @event load
32201     * Fires when this node is loaded
32202     * @param {Node} this This node
32203     */
32204     /**
32205      * The loader used by this node (defaults to using the tree's defined loader)
32206      * @type TreeLoader
32207      * @property loader
32208      */
32209 };
32210 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32211     expand : function(deep, anim, callback){
32212         if(this.loading){ // if an async load is already running, waiting til it's done
32213             var timer;
32214             var f = function(){
32215                 if(!this.loading){ // done loading
32216                     clearInterval(timer);
32217                     this.expand(deep, anim, callback);
32218                 }
32219             }.createDelegate(this);
32220             timer = setInterval(f, 200);
32221             return;
32222         }
32223         if(!this.loaded){
32224             if(this.fireEvent("beforeload", this) === false){
32225                 return;
32226             }
32227             this.loading = true;
32228             this.ui.beforeLoad(this);
32229             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32230             if(loader){
32231                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32232                 return;
32233             }
32234         }
32235         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32236     },
32237     
32238     /**
32239      * Returns true if this node is currently loading
32240      * @return {Boolean}
32241      */
32242     isLoading : function(){
32243         return this.loading;  
32244     },
32245     
32246     loadComplete : function(deep, anim, callback){
32247         this.loading = false;
32248         this.loaded = true;
32249         this.ui.afterLoad(this);
32250         this.fireEvent("load", this);
32251         this.expand(deep, anim, callback);
32252     },
32253     
32254     /**
32255      * Returns true if this node has been loaded
32256      * @return {Boolean}
32257      */
32258     isLoaded : function(){
32259         return this.loaded;
32260     },
32261     
32262     hasChildNodes : function(){
32263         if(!this.isLeaf() && !this.loaded){
32264             return true;
32265         }else{
32266             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32267         }
32268     },
32269
32270     /**
32271      * Trigger a reload for this node
32272      * @param {Function} callback
32273      */
32274     reload : function(callback){
32275         this.collapse(false, false);
32276         while(this.firstChild){
32277             this.removeChild(this.firstChild);
32278         }
32279         this.childrenRendered = false;
32280         this.loaded = false;
32281         if(this.isHiddenRoot()){
32282             this.expanded = false;
32283         }
32284         this.expand(false, false, callback);
32285     }
32286 });/*
32287  * Based on:
32288  * Ext JS Library 1.1.1
32289  * Copyright(c) 2006-2007, Ext JS, LLC.
32290  *
32291  * Originally Released Under LGPL - original licence link has changed is not relivant.
32292  *
32293  * Fork - LGPL
32294  * <script type="text/javascript">
32295  */
32296  
32297 /**
32298  * @class Roo.tree.TreeNodeUI
32299  * @constructor
32300  * @param {Object} node The node to render
32301  * The TreeNode UI implementation is separate from the
32302  * tree implementation. Unless you are customizing the tree UI,
32303  * you should never have to use this directly.
32304  */
32305 Roo.tree.TreeNodeUI = function(node){
32306     this.node = node;
32307     this.rendered = false;
32308     this.animating = false;
32309     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32310 };
32311
32312 Roo.tree.TreeNodeUI.prototype = {
32313     removeChild : function(node){
32314         if(this.rendered){
32315             this.ctNode.removeChild(node.ui.getEl());
32316         }
32317     },
32318
32319     beforeLoad : function(){
32320          this.addClass("x-tree-node-loading");
32321     },
32322
32323     afterLoad : function(){
32324          this.removeClass("x-tree-node-loading");
32325     },
32326
32327     onTextChange : function(node, text, oldText){
32328         if(this.rendered){
32329             this.textNode.innerHTML = text;
32330         }
32331     },
32332
32333     onDisableChange : function(node, state){
32334         this.disabled = state;
32335         if(state){
32336             this.addClass("x-tree-node-disabled");
32337         }else{
32338             this.removeClass("x-tree-node-disabled");
32339         }
32340     },
32341
32342     onSelectedChange : function(state){
32343         if(state){
32344             this.focus();
32345             this.addClass("x-tree-selected");
32346         }else{
32347             //this.blur();
32348             this.removeClass("x-tree-selected");
32349         }
32350     },
32351
32352     onMove : function(tree, node, oldParent, newParent, index, refNode){
32353         this.childIndent = null;
32354         if(this.rendered){
32355             var targetNode = newParent.ui.getContainer();
32356             if(!targetNode){//target not rendered
32357                 this.holder = document.createElement("div");
32358                 this.holder.appendChild(this.wrap);
32359                 return;
32360             }
32361             var insertBefore = refNode ? refNode.ui.getEl() : null;
32362             if(insertBefore){
32363                 targetNode.insertBefore(this.wrap, insertBefore);
32364             }else{
32365                 targetNode.appendChild(this.wrap);
32366             }
32367             this.node.renderIndent(true);
32368         }
32369     },
32370
32371     addClass : function(cls){
32372         if(this.elNode){
32373             Roo.fly(this.elNode).addClass(cls);
32374         }
32375     },
32376
32377     removeClass : function(cls){
32378         if(this.elNode){
32379             Roo.fly(this.elNode).removeClass(cls);
32380         }
32381     },
32382
32383     remove : function(){
32384         if(this.rendered){
32385             this.holder = document.createElement("div");
32386             this.holder.appendChild(this.wrap);
32387         }
32388     },
32389
32390     fireEvent : function(){
32391         return this.node.fireEvent.apply(this.node, arguments);
32392     },
32393
32394     initEvents : function(){
32395         this.node.on("move", this.onMove, this);
32396         var E = Roo.EventManager;
32397         var a = this.anchor;
32398
32399         var el = Roo.fly(a, '_treeui');
32400
32401         if(Roo.isOpera){ // opera render bug ignores the CSS
32402             el.setStyle("text-decoration", "none");
32403         }
32404
32405         el.on("click", this.onClick, this);
32406         el.on("dblclick", this.onDblClick, this);
32407
32408         if(this.checkbox){
32409             Roo.EventManager.on(this.checkbox,
32410                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32411         }
32412
32413         el.on("contextmenu", this.onContextMenu, this);
32414
32415         var icon = Roo.fly(this.iconNode);
32416         icon.on("click", this.onClick, this);
32417         icon.on("dblclick", this.onDblClick, this);
32418         icon.on("contextmenu", this.onContextMenu, this);
32419         E.on(this.ecNode, "click", this.ecClick, this, true);
32420
32421         if(this.node.disabled){
32422             this.addClass("x-tree-node-disabled");
32423         }
32424         if(this.node.hidden){
32425             this.addClass("x-tree-node-disabled");
32426         }
32427         var ot = this.node.getOwnerTree();
32428         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32429         if(dd && (!this.node.isRoot || ot.rootVisible)){
32430             Roo.dd.Registry.register(this.elNode, {
32431                 node: this.node,
32432                 handles: this.getDDHandles(),
32433                 isHandle: false
32434             });
32435         }
32436     },
32437
32438     getDDHandles : function(){
32439         return [this.iconNode, this.textNode];
32440     },
32441
32442     hide : function(){
32443         if(this.rendered){
32444             this.wrap.style.display = "none";
32445         }
32446     },
32447
32448     show : function(){
32449         if(this.rendered){
32450             this.wrap.style.display = "";
32451         }
32452     },
32453
32454     onContextMenu : function(e){
32455         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32456             e.preventDefault();
32457             this.focus();
32458             this.fireEvent("contextmenu", this.node, e);
32459         }
32460     },
32461
32462     onClick : function(e){
32463         if(this.dropping){
32464             e.stopEvent();
32465             return;
32466         }
32467         if(this.fireEvent("beforeclick", this.node, e) !== false){
32468             if(!this.disabled && this.node.attributes.href){
32469                 this.fireEvent("click", this.node, e);
32470                 return;
32471             }
32472             e.preventDefault();
32473             if(this.disabled){
32474                 return;
32475             }
32476
32477             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32478                 this.node.toggle();
32479             }
32480
32481             this.fireEvent("click", this.node, e);
32482         }else{
32483             e.stopEvent();
32484         }
32485     },
32486
32487     onDblClick : function(e){
32488         e.preventDefault();
32489         if(this.disabled){
32490             return;
32491         }
32492         if(this.checkbox){
32493             this.toggleCheck();
32494         }
32495         if(!this.animating && this.node.hasChildNodes()){
32496             this.node.toggle();
32497         }
32498         this.fireEvent("dblclick", this.node, e);
32499     },
32500
32501     onCheckChange : function(){
32502         var checked = this.checkbox.checked;
32503         this.node.attributes.checked = checked;
32504         this.fireEvent('checkchange', this.node, checked);
32505     },
32506
32507     ecClick : function(e){
32508         if(!this.animating && this.node.hasChildNodes()){
32509             this.node.toggle();
32510         }
32511     },
32512
32513     startDrop : function(){
32514         this.dropping = true;
32515     },
32516
32517     // delayed drop so the click event doesn't get fired on a drop
32518     endDrop : function(){
32519        setTimeout(function(){
32520            this.dropping = false;
32521        }.createDelegate(this), 50);
32522     },
32523
32524     expand : function(){
32525         this.updateExpandIcon();
32526         this.ctNode.style.display = "";
32527     },
32528
32529     focus : function(){
32530         if(!this.node.preventHScroll){
32531             try{this.anchor.focus();
32532             }catch(e){}
32533         }else if(!Roo.isIE){
32534             try{
32535                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32536                 var l = noscroll.scrollLeft;
32537                 this.anchor.focus();
32538                 noscroll.scrollLeft = l;
32539             }catch(e){}
32540         }
32541     },
32542
32543     toggleCheck : function(value){
32544         var cb = this.checkbox;
32545         if(cb){
32546             cb.checked = (value === undefined ? !cb.checked : value);
32547         }
32548     },
32549
32550     blur : function(){
32551         try{
32552             this.anchor.blur();
32553         }catch(e){}
32554     },
32555
32556     animExpand : function(callback){
32557         var ct = Roo.get(this.ctNode);
32558         ct.stopFx();
32559         if(!this.node.hasChildNodes()){
32560             this.updateExpandIcon();
32561             this.ctNode.style.display = "";
32562             Roo.callback(callback);
32563             return;
32564         }
32565         this.animating = true;
32566         this.updateExpandIcon();
32567
32568         ct.slideIn('t', {
32569            callback : function(){
32570                this.animating = false;
32571                Roo.callback(callback);
32572             },
32573             scope: this,
32574             duration: this.node.ownerTree.duration || .25
32575         });
32576     },
32577
32578     highlight : function(){
32579         var tree = this.node.getOwnerTree();
32580         Roo.fly(this.wrap).highlight(
32581             tree.hlColor || "C3DAF9",
32582             {endColor: tree.hlBaseColor}
32583         );
32584     },
32585
32586     collapse : function(){
32587         this.updateExpandIcon();
32588         this.ctNode.style.display = "none";
32589     },
32590
32591     animCollapse : function(callback){
32592         var ct = Roo.get(this.ctNode);
32593         ct.enableDisplayMode('block');
32594         ct.stopFx();
32595
32596         this.animating = true;
32597         this.updateExpandIcon();
32598
32599         ct.slideOut('t', {
32600             callback : function(){
32601                this.animating = false;
32602                Roo.callback(callback);
32603             },
32604             scope: this,
32605             duration: this.node.ownerTree.duration || .25
32606         });
32607     },
32608
32609     getContainer : function(){
32610         return this.ctNode;
32611     },
32612
32613     getEl : function(){
32614         return this.wrap;
32615     },
32616
32617     appendDDGhost : function(ghostNode){
32618         ghostNode.appendChild(this.elNode.cloneNode(true));
32619     },
32620
32621     getDDRepairXY : function(){
32622         return Roo.lib.Dom.getXY(this.iconNode);
32623     },
32624
32625     onRender : function(){
32626         this.render();
32627     },
32628
32629     render : function(bulkRender){
32630         var n = this.node, a = n.attributes;
32631         var targetNode = n.parentNode ?
32632               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32633
32634         if(!this.rendered){
32635             this.rendered = true;
32636
32637             this.renderElements(n, a, targetNode, bulkRender);
32638
32639             if(a.qtip){
32640                if(this.textNode.setAttributeNS){
32641                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32642                    if(a.qtipTitle){
32643                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32644                    }
32645                }else{
32646                    this.textNode.setAttribute("ext:qtip", a.qtip);
32647                    if(a.qtipTitle){
32648                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32649                    }
32650                }
32651             }else if(a.qtipCfg){
32652                 a.qtipCfg.target = Roo.id(this.textNode);
32653                 Roo.QuickTips.register(a.qtipCfg);
32654             }
32655             this.initEvents();
32656             if(!this.node.expanded){
32657                 this.updateExpandIcon();
32658             }
32659         }else{
32660             if(bulkRender === true) {
32661                 targetNode.appendChild(this.wrap);
32662             }
32663         }
32664     },
32665
32666     renderElements : function(n, a, targetNode, bulkRender)
32667     {
32668         // add some indent caching, this helps performance when rendering a large tree
32669         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32670         var t = n.getOwnerTree();
32671         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32672         if (typeof(n.attributes.html) != 'undefined') {
32673             txt = n.attributes.html;
32674         }
32675         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32676         var cb = typeof a.checked == 'boolean';
32677         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32678         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32679             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32680             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32681             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32682             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32683             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32684              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32685                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32686             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32687             "</li>"];
32688
32689         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32690             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32691                                 n.nextSibling.ui.getEl(), buf.join(""));
32692         }else{
32693             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32694         }
32695
32696         this.elNode = this.wrap.childNodes[0];
32697         this.ctNode = this.wrap.childNodes[1];
32698         var cs = this.elNode.childNodes;
32699         this.indentNode = cs[0];
32700         this.ecNode = cs[1];
32701         this.iconNode = cs[2];
32702         var index = 3;
32703         if(cb){
32704             this.checkbox = cs[3];
32705             index++;
32706         }
32707         this.anchor = cs[index];
32708         this.textNode = cs[index].firstChild;
32709     },
32710
32711     getAnchor : function(){
32712         return this.anchor;
32713     },
32714
32715     getTextEl : function(){
32716         return this.textNode;
32717     },
32718
32719     getIconEl : function(){
32720         return this.iconNode;
32721     },
32722
32723     isChecked : function(){
32724         return this.checkbox ? this.checkbox.checked : false;
32725     },
32726
32727     updateExpandIcon : function(){
32728         if(this.rendered){
32729             var n = this.node, c1, c2;
32730             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32731             var hasChild = n.hasChildNodes();
32732             if(hasChild){
32733                 if(n.expanded){
32734                     cls += "-minus";
32735                     c1 = "x-tree-node-collapsed";
32736                     c2 = "x-tree-node-expanded";
32737                 }else{
32738                     cls += "-plus";
32739                     c1 = "x-tree-node-expanded";
32740                     c2 = "x-tree-node-collapsed";
32741                 }
32742                 if(this.wasLeaf){
32743                     this.removeClass("x-tree-node-leaf");
32744                     this.wasLeaf = false;
32745                 }
32746                 if(this.c1 != c1 || this.c2 != c2){
32747                     Roo.fly(this.elNode).replaceClass(c1, c2);
32748                     this.c1 = c1; this.c2 = c2;
32749                 }
32750             }else{
32751                 // this changes non-leafs into leafs if they have no children.
32752                 // it's not very rational behaviour..
32753                 
32754                 if(!this.wasLeaf && this.node.leaf){
32755                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32756                     delete this.c1;
32757                     delete this.c2;
32758                     this.wasLeaf = true;
32759                 }
32760             }
32761             var ecc = "x-tree-ec-icon "+cls;
32762             if(this.ecc != ecc){
32763                 this.ecNode.className = ecc;
32764                 this.ecc = ecc;
32765             }
32766         }
32767     },
32768
32769     getChildIndent : function(){
32770         if(!this.childIndent){
32771             var buf = [];
32772             var p = this.node;
32773             while(p){
32774                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32775                     if(!p.isLast()) {
32776                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32777                     } else {
32778                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32779                     }
32780                 }
32781                 p = p.parentNode;
32782             }
32783             this.childIndent = buf.join("");
32784         }
32785         return this.childIndent;
32786     },
32787
32788     renderIndent : function(){
32789         if(this.rendered){
32790             var indent = "";
32791             var p = this.node.parentNode;
32792             if(p){
32793                 indent = p.ui.getChildIndent();
32794             }
32795             if(this.indentMarkup != indent){ // don't rerender if not required
32796                 this.indentNode.innerHTML = indent;
32797                 this.indentMarkup = indent;
32798             }
32799             this.updateExpandIcon();
32800         }
32801     }
32802 };
32803
32804 Roo.tree.RootTreeNodeUI = function(){
32805     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32806 };
32807 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32808     render : function(){
32809         if(!this.rendered){
32810             var targetNode = this.node.ownerTree.innerCt.dom;
32811             this.node.expanded = true;
32812             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32813             this.wrap = this.ctNode = targetNode.firstChild;
32814         }
32815     },
32816     collapse : function(){
32817     },
32818     expand : function(){
32819     }
32820 });/*
32821  * Based on:
32822  * Ext JS Library 1.1.1
32823  * Copyright(c) 2006-2007, Ext JS, LLC.
32824  *
32825  * Originally Released Under LGPL - original licence link has changed is not relivant.
32826  *
32827  * Fork - LGPL
32828  * <script type="text/javascript">
32829  */
32830 /**
32831  * @class Roo.tree.TreeLoader
32832  * @extends Roo.util.Observable
32833  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32834  * nodes from a specified URL. The response must be a javascript Array definition
32835  * who's elements are node definition objects. eg:
32836  * <pre><code>
32837    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32838     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32839 </code></pre>
32840  * <br><br>
32841  * A server request is sent, and child nodes are loaded only when a node is expanded.
32842  * The loading node's id is passed to the server under the parameter name "node" to
32843  * enable the server to produce the correct child nodes.
32844  * <br><br>
32845  * To pass extra parameters, an event handler may be attached to the "beforeload"
32846  * event, and the parameters specified in the TreeLoader's baseParams property:
32847  * <pre><code>
32848     myTreeLoader.on("beforeload", function(treeLoader, node) {
32849         this.baseParams.category = node.attributes.category;
32850     }, this);
32851 </code></pre><
32852  * This would pass an HTTP parameter called "category" to the server containing
32853  * the value of the Node's "category" attribute.
32854  * @constructor
32855  * Creates a new Treeloader.
32856  * @param {Object} config A config object containing config properties.
32857  */
32858 Roo.tree.TreeLoader = function(config){
32859     this.baseParams = {};
32860     this.requestMethod = "POST";
32861     Roo.apply(this, config);
32862
32863     this.addEvents({
32864     
32865         /**
32866          * @event beforeload
32867          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32868          * @param {Object} This TreeLoader object.
32869          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32870          * @param {Object} callback The callback function specified in the {@link #load} call.
32871          */
32872         beforeload : true,
32873         /**
32874          * @event load
32875          * Fires when the node has been successfuly loaded.
32876          * @param {Object} This TreeLoader object.
32877          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32878          * @param {Object} response The response object containing the data from the server.
32879          */
32880         load : true,
32881         /**
32882          * @event loadexception
32883          * Fires if the network request failed.
32884          * @param {Object} This TreeLoader object.
32885          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32886          * @param {Object} response The response object containing the data from the server.
32887          */
32888         loadexception : true,
32889         /**
32890          * @event create
32891          * Fires before a node is created, enabling you to return custom Node types 
32892          * @param {Object} This TreeLoader object.
32893          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32894          */
32895         create : true
32896     });
32897
32898     Roo.tree.TreeLoader.superclass.constructor.call(this);
32899 };
32900
32901 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32902     /**
32903     * @cfg {String} dataUrl The URL from which to request a Json string which
32904     * specifies an array of node definition object representing the child nodes
32905     * to be loaded.
32906     */
32907     /**
32908     * @cfg {Object} baseParams (optional) An object containing properties which
32909     * specify HTTP parameters to be passed to each request for child nodes.
32910     */
32911     /**
32912     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32913     * created by this loader. If the attributes sent by the server have an attribute in this object,
32914     * they take priority.
32915     */
32916     /**
32917     * @cfg {Object} uiProviders (optional) An object containing properties which
32918     * 
32919     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32920     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32921     * <i>uiProvider</i> attribute of a returned child node is a string rather
32922     * than a reference to a TreeNodeUI implementation, this that string value
32923     * is used as a property name in the uiProviders object. You can define the provider named
32924     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32925     */
32926     uiProviders : {},
32927
32928     /**
32929     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32930     * child nodes before loading.
32931     */
32932     clearOnLoad : true,
32933
32934     /**
32935     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32936     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32937     * Grid query { data : [ .....] }
32938     */
32939     
32940     root : false,
32941      /**
32942     * @cfg {String} queryParam (optional) 
32943     * Name of the query as it will be passed on the querystring (defaults to 'node')
32944     * eg. the request will be ?node=[id]
32945     */
32946     
32947     
32948     queryParam: false,
32949     
32950     /**
32951      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32952      * This is called automatically when a node is expanded, but may be used to reload
32953      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32954      * @param {Roo.tree.TreeNode} node
32955      * @param {Function} callback
32956      */
32957     load : function(node, callback){
32958         if(this.clearOnLoad){
32959             while(node.firstChild){
32960                 node.removeChild(node.firstChild);
32961             }
32962         }
32963         if(node.attributes.children){ // preloaded json children
32964             var cs = node.attributes.children;
32965             for(var i = 0, len = cs.length; i < len; i++){
32966                 node.appendChild(this.createNode(cs[i]));
32967             }
32968             if(typeof callback == "function"){
32969                 callback();
32970             }
32971         }else if(this.dataUrl){
32972             this.requestData(node, callback);
32973         }
32974     },
32975
32976     getParams: function(node){
32977         var buf = [], bp = this.baseParams;
32978         for(var key in bp){
32979             if(typeof bp[key] != "function"){
32980                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32981             }
32982         }
32983         var n = this.queryParam === false ? 'node' : this.queryParam;
32984         buf.push(n + "=", encodeURIComponent(node.id));
32985         return buf.join("");
32986     },
32987
32988     requestData : function(node, callback){
32989         if(this.fireEvent("beforeload", this, node, callback) !== false){
32990             this.transId = Roo.Ajax.request({
32991                 method:this.requestMethod,
32992                 url: this.dataUrl||this.url,
32993                 success: this.handleResponse,
32994                 failure: this.handleFailure,
32995                 scope: this,
32996                 argument: {callback: callback, node: node},
32997                 params: this.getParams(node)
32998             });
32999         }else{
33000             // if the load is cancelled, make sure we notify
33001             // the node that we are done
33002             if(typeof callback == "function"){
33003                 callback();
33004             }
33005         }
33006     },
33007
33008     isLoading : function(){
33009         return this.transId ? true : false;
33010     },
33011
33012     abort : function(){
33013         if(this.isLoading()){
33014             Roo.Ajax.abort(this.transId);
33015         }
33016     },
33017
33018     // private
33019     createNode : function(attr)
33020     {
33021         // apply baseAttrs, nice idea Corey!
33022         if(this.baseAttrs){
33023             Roo.applyIf(attr, this.baseAttrs);
33024         }
33025         if(this.applyLoader !== false){
33026             attr.loader = this;
33027         }
33028         // uiProvider = depreciated..
33029         
33030         if(typeof(attr.uiProvider) == 'string'){
33031            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33032                 /**  eval:var:attr */ eval(attr.uiProvider);
33033         }
33034         if(typeof(this.uiProviders['default']) != 'undefined') {
33035             attr.uiProvider = this.uiProviders['default'];
33036         }
33037         
33038         this.fireEvent('create', this, attr);
33039         
33040         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33041         return(attr.leaf ?
33042                         new Roo.tree.TreeNode(attr) :
33043                         new Roo.tree.AsyncTreeNode(attr));
33044     },
33045
33046     processResponse : function(response, node, callback)
33047     {
33048         var json = response.responseText;
33049         try {
33050             
33051             var o = Roo.decode(json);
33052             
33053             if (!o.success) {
33054                 // it's a failure condition.
33055                 var a = response.argument;
33056                 this.fireEvent("loadexception", this, a.node, response);
33057                 Roo.log("Load failed - should have a handler really");
33058                 return;
33059             }
33060             
33061             if (this.root !== false) {
33062                 o = o[this.root];
33063             }
33064             
33065             for(var i = 0, len = o.length; i < len; i++){
33066                 var n = this.createNode(o[i]);
33067                 if(n){
33068                     node.appendChild(n);
33069                 }
33070             }
33071             if(typeof callback == "function"){
33072                 callback(this, node);
33073             }
33074         }catch(e){
33075             this.handleFailure(response);
33076         }
33077     },
33078
33079     handleResponse : function(response){
33080         this.transId = false;
33081         var a = response.argument;
33082         this.processResponse(response, a.node, a.callback);
33083         this.fireEvent("load", this, a.node, response);
33084     },
33085
33086     handleFailure : function(response)
33087     {
33088         // should handle failure better..
33089         this.transId = false;
33090         var a = response.argument;
33091         this.fireEvent("loadexception", this, a.node, response);
33092         if(typeof a.callback == "function"){
33093             a.callback(this, a.node);
33094         }
33095     }
33096 });/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106
33107 /**
33108 * @class Roo.tree.TreeFilter
33109 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33110 * @param {TreePanel} tree
33111 * @param {Object} config (optional)
33112  */
33113 Roo.tree.TreeFilter = function(tree, config){
33114     this.tree = tree;
33115     this.filtered = {};
33116     Roo.apply(this, config);
33117 };
33118
33119 Roo.tree.TreeFilter.prototype = {
33120     clearBlank:false,
33121     reverse:false,
33122     autoClear:false,
33123     remove:false,
33124
33125      /**
33126      * Filter the data by a specific attribute.
33127      * @param {String/RegExp} value Either string that the attribute value
33128      * should start with or a RegExp to test against the attribute
33129      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33130      * @param {TreeNode} startNode (optional) The node to start the filter at.
33131      */
33132     filter : function(value, attr, startNode){
33133         attr = attr || "text";
33134         var f;
33135         if(typeof value == "string"){
33136             var vlen = value.length;
33137             // auto clear empty filter
33138             if(vlen == 0 && this.clearBlank){
33139                 this.clear();
33140                 return;
33141             }
33142             value = value.toLowerCase();
33143             f = function(n){
33144                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33145             };
33146         }else if(value.exec){ // regex?
33147             f = function(n){
33148                 return value.test(n.attributes[attr]);
33149             };
33150         }else{
33151             throw 'Illegal filter type, must be string or regex';
33152         }
33153         this.filterBy(f, null, startNode);
33154         },
33155
33156     /**
33157      * Filter by a function. The passed function will be called with each
33158      * node in the tree (or from the startNode). If the function returns true, the node is kept
33159      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33160      * @param {Function} fn The filter function
33161      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33162      */
33163     filterBy : function(fn, scope, startNode){
33164         startNode = startNode || this.tree.root;
33165         if(this.autoClear){
33166             this.clear();
33167         }
33168         var af = this.filtered, rv = this.reverse;
33169         var f = function(n){
33170             if(n == startNode){
33171                 return true;
33172             }
33173             if(af[n.id]){
33174                 return false;
33175             }
33176             var m = fn.call(scope || n, n);
33177             if(!m || rv){
33178                 af[n.id] = n;
33179                 n.ui.hide();
33180                 return false;
33181             }
33182             return true;
33183         };
33184         startNode.cascade(f);
33185         if(this.remove){
33186            for(var id in af){
33187                if(typeof id != "function"){
33188                    var n = af[id];
33189                    if(n && n.parentNode){
33190                        n.parentNode.removeChild(n);
33191                    }
33192                }
33193            }
33194         }
33195     },
33196
33197     /**
33198      * Clears the current filter. Note: with the "remove" option
33199      * set a filter cannot be cleared.
33200      */
33201     clear : function(){
33202         var t = this.tree;
33203         var af = this.filtered;
33204         for(var id in af){
33205             if(typeof id != "function"){
33206                 var n = af[id];
33207                 if(n){
33208                     n.ui.show();
33209                 }
33210             }
33211         }
33212         this.filtered = {};
33213     }
33214 };
33215 /*
33216  * Based on:
33217  * Ext JS Library 1.1.1
33218  * Copyright(c) 2006-2007, Ext JS, LLC.
33219  *
33220  * Originally Released Under LGPL - original licence link has changed is not relivant.
33221  *
33222  * Fork - LGPL
33223  * <script type="text/javascript">
33224  */
33225  
33226
33227 /**
33228  * @class Roo.tree.TreeSorter
33229  * Provides sorting of nodes in a TreePanel
33230  * 
33231  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33232  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33233  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33234  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33235  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33236  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33237  * @constructor
33238  * @param {TreePanel} tree
33239  * @param {Object} config
33240  */
33241 Roo.tree.TreeSorter = function(tree, config){
33242     Roo.apply(this, config);
33243     tree.on("beforechildrenrendered", this.doSort, this);
33244     tree.on("append", this.updateSort, this);
33245     tree.on("insert", this.updateSort, this);
33246     
33247     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33248     var p = this.property || "text";
33249     var sortType = this.sortType;
33250     var fs = this.folderSort;
33251     var cs = this.caseSensitive === true;
33252     var leafAttr = this.leafAttr || 'leaf';
33253
33254     this.sortFn = function(n1, n2){
33255         if(fs){
33256             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33257                 return 1;
33258             }
33259             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33260                 return -1;
33261             }
33262         }
33263         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33264         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33265         if(v1 < v2){
33266                         return dsc ? +1 : -1;
33267                 }else if(v1 > v2){
33268                         return dsc ? -1 : +1;
33269         }else{
33270                 return 0;
33271         }
33272     };
33273 };
33274
33275 Roo.tree.TreeSorter.prototype = {
33276     doSort : function(node){
33277         node.sort(this.sortFn);
33278     },
33279     
33280     compareNodes : function(n1, n2){
33281         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33282     },
33283     
33284     updateSort : function(tree, node){
33285         if(node.childrenRendered){
33286             this.doSort.defer(1, this, [node]);
33287         }
33288     }
33289 };/*
33290  * Based on:
33291  * Ext JS Library 1.1.1
33292  * Copyright(c) 2006-2007, Ext JS, LLC.
33293  *
33294  * Originally Released Under LGPL - original licence link has changed is not relivant.
33295  *
33296  * Fork - LGPL
33297  * <script type="text/javascript">
33298  */
33299
33300 if(Roo.dd.DropZone){
33301     
33302 Roo.tree.TreeDropZone = function(tree, config){
33303     this.allowParentInsert = false;
33304     this.allowContainerDrop = false;
33305     this.appendOnly = false;
33306     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33307     this.tree = tree;
33308     this.lastInsertClass = "x-tree-no-status";
33309     this.dragOverData = {};
33310 };
33311
33312 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33313     ddGroup : "TreeDD",
33314     
33315     expandDelay : 1000,
33316     
33317     expandNode : function(node){
33318         if(node.hasChildNodes() && !node.isExpanded()){
33319             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33320         }
33321     },
33322     
33323     queueExpand : function(node){
33324         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33325     },
33326     
33327     cancelExpand : function(){
33328         if(this.expandProcId){
33329             clearTimeout(this.expandProcId);
33330             this.expandProcId = false;
33331         }
33332     },
33333     
33334     isValidDropPoint : function(n, pt, dd, e, data){
33335         if(!n || !data){ return false; }
33336         var targetNode = n.node;
33337         var dropNode = data.node;
33338         // default drop rules
33339         if(!(targetNode && targetNode.isTarget && pt)){
33340             return false;
33341         }
33342         if(pt == "append" && targetNode.allowChildren === false){
33343             return false;
33344         }
33345         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33346             return false;
33347         }
33348         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33349             return false;
33350         }
33351         // reuse the object
33352         var overEvent = this.dragOverData;
33353         overEvent.tree = this.tree;
33354         overEvent.target = targetNode;
33355         overEvent.data = data;
33356         overEvent.point = pt;
33357         overEvent.source = dd;
33358         overEvent.rawEvent = e;
33359         overEvent.dropNode = dropNode;
33360         overEvent.cancel = false;  
33361         var result = this.tree.fireEvent("nodedragover", overEvent);
33362         return overEvent.cancel === false && result !== false;
33363     },
33364     
33365     getDropPoint : function(e, n, dd){
33366         var tn = n.node;
33367         if(tn.isRoot){
33368             return tn.allowChildren !== false ? "append" : false; // always append for root
33369         }
33370         var dragEl = n.ddel;
33371         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33372         var y = Roo.lib.Event.getPageY(e);
33373         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33374         
33375         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33376         var noAppend = tn.allowChildren === false;
33377         if(this.appendOnly || tn.parentNode.allowChildren === false){
33378             return noAppend ? false : "append";
33379         }
33380         var noBelow = false;
33381         if(!this.allowParentInsert){
33382             noBelow = tn.hasChildNodes() && tn.isExpanded();
33383         }
33384         var q = (b - t) / (noAppend ? 2 : 3);
33385         if(y >= t && y < (t + q)){
33386             return "above";
33387         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33388             return "below";
33389         }else{
33390             return "append";
33391         }
33392     },
33393     
33394     onNodeEnter : function(n, dd, e, data){
33395         this.cancelExpand();
33396     },
33397     
33398     onNodeOver : function(n, dd, e, data){
33399         var pt = this.getDropPoint(e, n, dd);
33400         var node = n.node;
33401         
33402         // auto node expand check
33403         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33404             this.queueExpand(node);
33405         }else if(pt != "append"){
33406             this.cancelExpand();
33407         }
33408         
33409         // set the insert point style on the target node
33410         var returnCls = this.dropNotAllowed;
33411         if(this.isValidDropPoint(n, pt, dd, e, data)){
33412            if(pt){
33413                var el = n.ddel;
33414                var cls;
33415                if(pt == "above"){
33416                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33417                    cls = "x-tree-drag-insert-above";
33418                }else if(pt == "below"){
33419                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33420                    cls = "x-tree-drag-insert-below";
33421                }else{
33422                    returnCls = "x-tree-drop-ok-append";
33423                    cls = "x-tree-drag-append";
33424                }
33425                if(this.lastInsertClass != cls){
33426                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33427                    this.lastInsertClass = cls;
33428                }
33429            }
33430        }
33431        return returnCls;
33432     },
33433     
33434     onNodeOut : function(n, dd, e, data){
33435         this.cancelExpand();
33436         this.removeDropIndicators(n);
33437     },
33438     
33439     onNodeDrop : function(n, dd, e, data){
33440         var point = this.getDropPoint(e, n, dd);
33441         var targetNode = n.node;
33442         targetNode.ui.startDrop();
33443         if(!this.isValidDropPoint(n, point, dd, e, data)){
33444             targetNode.ui.endDrop();
33445             return false;
33446         }
33447         // first try to find the drop node
33448         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33449         var dropEvent = {
33450             tree : this.tree,
33451             target: targetNode,
33452             data: data,
33453             point: point,
33454             source: dd,
33455             rawEvent: e,
33456             dropNode: dropNode,
33457             cancel: !dropNode   
33458         };
33459         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33460         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33461             targetNode.ui.endDrop();
33462             return false;
33463         }
33464         // allow target changing
33465         targetNode = dropEvent.target;
33466         if(point == "append" && !targetNode.isExpanded()){
33467             targetNode.expand(false, null, function(){
33468                 this.completeDrop(dropEvent);
33469             }.createDelegate(this));
33470         }else{
33471             this.completeDrop(dropEvent);
33472         }
33473         return true;
33474     },
33475     
33476     completeDrop : function(de){
33477         var ns = de.dropNode, p = de.point, t = de.target;
33478         if(!(ns instanceof Array)){
33479             ns = [ns];
33480         }
33481         var n;
33482         for(var i = 0, len = ns.length; i < len; i++){
33483             n = ns[i];
33484             if(p == "above"){
33485                 t.parentNode.insertBefore(n, t);
33486             }else if(p == "below"){
33487                 t.parentNode.insertBefore(n, t.nextSibling);
33488             }else{
33489                 t.appendChild(n);
33490             }
33491         }
33492         n.ui.focus();
33493         if(this.tree.hlDrop){
33494             n.ui.highlight();
33495         }
33496         t.ui.endDrop();
33497         this.tree.fireEvent("nodedrop", de);
33498     },
33499     
33500     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33501         if(this.tree.hlDrop){
33502             dropNode.ui.focus();
33503             dropNode.ui.highlight();
33504         }
33505         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33506     },
33507     
33508     getTree : function(){
33509         return this.tree;
33510     },
33511     
33512     removeDropIndicators : function(n){
33513         if(n && n.ddel){
33514             var el = n.ddel;
33515             Roo.fly(el).removeClass([
33516                     "x-tree-drag-insert-above",
33517                     "x-tree-drag-insert-below",
33518                     "x-tree-drag-append"]);
33519             this.lastInsertClass = "_noclass";
33520         }
33521     },
33522     
33523     beforeDragDrop : function(target, e, id){
33524         this.cancelExpand();
33525         return true;
33526     },
33527     
33528     afterRepair : function(data){
33529         if(data && Roo.enableFx){
33530             data.node.ui.highlight();
33531         }
33532         this.hideProxy();
33533     }    
33534 });
33535
33536 }
33537 /*
33538  * Based on:
33539  * Ext JS Library 1.1.1
33540  * Copyright(c) 2006-2007, Ext JS, LLC.
33541  *
33542  * Originally Released Under LGPL - original licence link has changed is not relivant.
33543  *
33544  * Fork - LGPL
33545  * <script type="text/javascript">
33546  */
33547  
33548
33549 if(Roo.dd.DragZone){
33550 Roo.tree.TreeDragZone = function(tree, config){
33551     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33552     this.tree = tree;
33553 };
33554
33555 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33556     ddGroup : "TreeDD",
33557     
33558     onBeforeDrag : function(data, e){
33559         var n = data.node;
33560         return n && n.draggable && !n.disabled;
33561     },
33562     
33563     onInitDrag : function(e){
33564         var data = this.dragData;
33565         this.tree.getSelectionModel().select(data.node);
33566         this.proxy.update("");
33567         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33568         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33569     },
33570     
33571     getRepairXY : function(e, data){
33572         return data.node.ui.getDDRepairXY();
33573     },
33574     
33575     onEndDrag : function(data, e){
33576         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33577     },
33578     
33579     onValidDrop : function(dd, e, id){
33580         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33581         this.hideProxy();
33582     },
33583     
33584     beforeInvalidDrop : function(e, id){
33585         // this scrolls the original position back into view
33586         var sm = this.tree.getSelectionModel();
33587         sm.clearSelections();
33588         sm.select(this.dragData.node);
33589     }
33590 });
33591 }/*
33592  * Based on:
33593  * Ext JS Library 1.1.1
33594  * Copyright(c) 2006-2007, Ext JS, LLC.
33595  *
33596  * Originally Released Under LGPL - original licence link has changed is not relivant.
33597  *
33598  * Fork - LGPL
33599  * <script type="text/javascript">
33600  */
33601 /**
33602  * @class Roo.tree.TreeEditor
33603  * @extends Roo.Editor
33604  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33605  * as the editor field.
33606  * @constructor
33607  * @param {Object} config (used to be the tree panel.)
33608  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33609  * 
33610  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33611  * @cfg {Roo.form.TextField|Object} field The field configuration
33612  *
33613  * 
33614  */
33615 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33616     var tree = config;
33617     var field;
33618     if (oldconfig) { // old style..
33619         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33620     } else {
33621         // new style..
33622         tree = config.tree;
33623         config.field = config.field  || {};
33624         config.field.xtype = 'TextField';
33625         field = Roo.factory(config.field, Roo.form);
33626     }
33627     config = config || {};
33628     
33629     
33630     this.addEvents({
33631         /**
33632          * @event beforenodeedit
33633          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33634          * false from the handler of this event.
33635          * @param {Editor} this
33636          * @param {Roo.tree.Node} node 
33637          */
33638         "beforenodeedit" : true
33639     });
33640     
33641     //Roo.log(config);
33642     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33643
33644     this.tree = tree;
33645
33646     tree.on('beforeclick', this.beforeNodeClick, this);
33647     tree.getTreeEl().on('mousedown', this.hide, this);
33648     this.on('complete', this.updateNode, this);
33649     this.on('beforestartedit', this.fitToTree, this);
33650     this.on('startedit', this.bindScroll, this, {delay:10});
33651     this.on('specialkey', this.onSpecialKey, this);
33652 };
33653
33654 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33655     /**
33656      * @cfg {String} alignment
33657      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33658      */
33659     alignment: "l-l",
33660     // inherit
33661     autoSize: false,
33662     /**
33663      * @cfg {Boolean} hideEl
33664      * True to hide the bound element while the editor is displayed (defaults to false)
33665      */
33666     hideEl : false,
33667     /**
33668      * @cfg {String} cls
33669      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33670      */
33671     cls: "x-small-editor x-tree-editor",
33672     /**
33673      * @cfg {Boolean} shim
33674      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33675      */
33676     shim:false,
33677     // inherit
33678     shadow:"frame",
33679     /**
33680      * @cfg {Number} maxWidth
33681      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33682      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33683      * scroll and client offsets into account prior to each edit.
33684      */
33685     maxWidth: 250,
33686
33687     editDelay : 350,
33688
33689     // private
33690     fitToTree : function(ed, el){
33691         var td = this.tree.getTreeEl().dom, nd = el.dom;
33692         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33693             td.scrollLeft = nd.offsetLeft;
33694         }
33695         var w = Math.min(
33696                 this.maxWidth,
33697                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33698         this.setSize(w, '');
33699         
33700         return this.fireEvent('beforenodeedit', this, this.editNode);
33701         
33702     },
33703
33704     // private
33705     triggerEdit : function(node){
33706         this.completeEdit();
33707         this.editNode = node;
33708         this.startEdit(node.ui.textNode, node.text);
33709     },
33710
33711     // private
33712     bindScroll : function(){
33713         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33714     },
33715
33716     // private
33717     beforeNodeClick : function(node, e){
33718         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33719         this.lastClick = new Date();
33720         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33721             e.stopEvent();
33722             this.triggerEdit(node);
33723             return false;
33724         }
33725         return true;
33726     },
33727
33728     // private
33729     updateNode : function(ed, value){
33730         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33731         this.editNode.setText(value);
33732     },
33733
33734     // private
33735     onHide : function(){
33736         Roo.tree.TreeEditor.superclass.onHide.call(this);
33737         if(this.editNode){
33738             this.editNode.ui.focus();
33739         }
33740     },
33741
33742     // private
33743     onSpecialKey : function(field, e){
33744         var k = e.getKey();
33745         if(k == e.ESC){
33746             e.stopEvent();
33747             this.cancelEdit();
33748         }else if(k == e.ENTER && !e.hasModifier()){
33749             e.stopEvent();
33750             this.completeEdit();
33751         }
33752     }
33753 });//<Script type="text/javascript">
33754 /*
33755  * Based on:
33756  * Ext JS Library 1.1.1
33757  * Copyright(c) 2006-2007, Ext JS, LLC.
33758  *
33759  * Originally Released Under LGPL - original licence link has changed is not relivant.
33760  *
33761  * Fork - LGPL
33762  * <script type="text/javascript">
33763  */
33764  
33765 /**
33766  * Not documented??? - probably should be...
33767  */
33768
33769 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33770     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33771     
33772     renderElements : function(n, a, targetNode, bulkRender){
33773         //consel.log("renderElements?");
33774         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33775
33776         var t = n.getOwnerTree();
33777         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33778         
33779         var cols = t.columns;
33780         var bw = t.borderWidth;
33781         var c = cols[0];
33782         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33783          var cb = typeof a.checked == "boolean";
33784         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33785         var colcls = 'x-t-' + tid + '-c0';
33786         var buf = [
33787             '<li class="x-tree-node">',
33788             
33789                 
33790                 '<div class="x-tree-node-el ', a.cls,'">',
33791                     // extran...
33792                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33793                 
33794                 
33795                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33796                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33797                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33798                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33799                            (a.iconCls ? ' '+a.iconCls : ''),
33800                            '" unselectable="on" />',
33801                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33802                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33803                              
33804                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33805                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33806                             '<span unselectable="on" qtip="' + tx + '">',
33807                              tx,
33808                              '</span></a>' ,
33809                     '</div>',
33810                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33811                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33812                  ];
33813         for(var i = 1, len = cols.length; i < len; i++){
33814             c = cols[i];
33815             colcls = 'x-t-' + tid + '-c' +i;
33816             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33817             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33818                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33819                       "</div>");
33820          }
33821          
33822          buf.push(
33823             '</a>',
33824             '<div class="x-clear"></div></div>',
33825             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33826             "</li>");
33827         
33828         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33829             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33830                                 n.nextSibling.ui.getEl(), buf.join(""));
33831         }else{
33832             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33833         }
33834         var el = this.wrap.firstChild;
33835         this.elRow = el;
33836         this.elNode = el.firstChild;
33837         this.ranchor = el.childNodes[1];
33838         this.ctNode = this.wrap.childNodes[1];
33839         var cs = el.firstChild.childNodes;
33840         this.indentNode = cs[0];
33841         this.ecNode = cs[1];
33842         this.iconNode = cs[2];
33843         var index = 3;
33844         if(cb){
33845             this.checkbox = cs[3];
33846             index++;
33847         }
33848         this.anchor = cs[index];
33849         
33850         this.textNode = cs[index].firstChild;
33851         
33852         //el.on("click", this.onClick, this);
33853         //el.on("dblclick", this.onDblClick, this);
33854         
33855         
33856        // console.log(this);
33857     },
33858     initEvents : function(){
33859         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33860         
33861             
33862         var a = this.ranchor;
33863
33864         var el = Roo.get(a);
33865
33866         if(Roo.isOpera){ // opera render bug ignores the CSS
33867             el.setStyle("text-decoration", "none");
33868         }
33869
33870         el.on("click", this.onClick, this);
33871         el.on("dblclick", this.onDblClick, this);
33872         el.on("contextmenu", this.onContextMenu, this);
33873         
33874     },
33875     
33876     /*onSelectedChange : function(state){
33877         if(state){
33878             this.focus();
33879             this.addClass("x-tree-selected");
33880         }else{
33881             //this.blur();
33882             this.removeClass("x-tree-selected");
33883         }
33884     },*/
33885     addClass : function(cls){
33886         if(this.elRow){
33887             Roo.fly(this.elRow).addClass(cls);
33888         }
33889         
33890     },
33891     
33892     
33893     removeClass : function(cls){
33894         if(this.elRow){
33895             Roo.fly(this.elRow).removeClass(cls);
33896         }
33897     }
33898
33899     
33900     
33901 });//<Script type="text/javascript">
33902
33903 /*
33904  * Based on:
33905  * Ext JS Library 1.1.1
33906  * Copyright(c) 2006-2007, Ext JS, LLC.
33907  *
33908  * Originally Released Under LGPL - original licence link has changed is not relivant.
33909  *
33910  * Fork - LGPL
33911  * <script type="text/javascript">
33912  */
33913  
33914
33915 /**
33916  * @class Roo.tree.ColumnTree
33917  * @extends Roo.data.TreePanel
33918  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33919  * @cfg {int} borderWidth  compined right/left border allowance
33920  * @constructor
33921  * @param {String/HTMLElement/Element} el The container element
33922  * @param {Object} config
33923  */
33924 Roo.tree.ColumnTree =  function(el, config)
33925 {
33926    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33927    this.addEvents({
33928         /**
33929         * @event resize
33930         * Fire this event on a container when it resizes
33931         * @param {int} w Width
33932         * @param {int} h Height
33933         */
33934        "resize" : true
33935     });
33936     this.on('resize', this.onResize, this);
33937 };
33938
33939 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33940     //lines:false,
33941     
33942     
33943     borderWidth: Roo.isBorderBox ? 0 : 2, 
33944     headEls : false,
33945     
33946     render : function(){
33947         // add the header.....
33948        
33949         Roo.tree.ColumnTree.superclass.render.apply(this);
33950         
33951         this.el.addClass('x-column-tree');
33952         
33953         this.headers = this.el.createChild(
33954             {cls:'x-tree-headers'},this.innerCt.dom);
33955    
33956         var cols = this.columns, c;
33957         var totalWidth = 0;
33958         this.headEls = [];
33959         var  len = cols.length;
33960         for(var i = 0; i < len; i++){
33961              c = cols[i];
33962              totalWidth += c.width;
33963             this.headEls.push(this.headers.createChild({
33964                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33965                  cn: {
33966                      cls:'x-tree-hd-text',
33967                      html: c.header
33968                  },
33969                  style:'width:'+(c.width-this.borderWidth)+'px;'
33970              }));
33971         }
33972         this.headers.createChild({cls:'x-clear'});
33973         // prevent floats from wrapping when clipped
33974         this.headers.setWidth(totalWidth);
33975         //this.innerCt.setWidth(totalWidth);
33976         this.innerCt.setStyle({ overflow: 'auto' });
33977         this.onResize(this.width, this.height);
33978              
33979         
33980     },
33981     onResize : function(w,h)
33982     {
33983         this.height = h;
33984         this.width = w;
33985         // resize cols..
33986         this.innerCt.setWidth(this.width);
33987         this.innerCt.setHeight(this.height-20);
33988         
33989         // headers...
33990         var cols = this.columns, c;
33991         var totalWidth = 0;
33992         var expEl = false;
33993         var len = cols.length;
33994         for(var i = 0; i < len; i++){
33995             c = cols[i];
33996             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33997                 // it's the expander..
33998                 expEl  = this.headEls[i];
33999                 continue;
34000             }
34001             totalWidth += c.width;
34002             
34003         }
34004         if (expEl) {
34005             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34006         }
34007         this.headers.setWidth(w-20);
34008
34009         
34010         
34011         
34012     }
34013 });
34014 /*
34015  * Based on:
34016  * Ext JS Library 1.1.1
34017  * Copyright(c) 2006-2007, Ext JS, LLC.
34018  *
34019  * Originally Released Under LGPL - original licence link has changed is not relivant.
34020  *
34021  * Fork - LGPL
34022  * <script type="text/javascript">
34023  */
34024  
34025 /**
34026  * @class Roo.menu.Menu
34027  * @extends Roo.util.Observable
34028  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34029  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34030  * @constructor
34031  * Creates a new Menu
34032  * @param {Object} config Configuration options
34033  */
34034 Roo.menu.Menu = function(config){
34035     Roo.apply(this, config);
34036     this.id = this.id || Roo.id();
34037     this.addEvents({
34038         /**
34039          * @event beforeshow
34040          * Fires before this menu is displayed
34041          * @param {Roo.menu.Menu} this
34042          */
34043         beforeshow : true,
34044         /**
34045          * @event beforehide
34046          * Fires before this menu is hidden
34047          * @param {Roo.menu.Menu} this
34048          */
34049         beforehide : true,
34050         /**
34051          * @event show
34052          * Fires after this menu is displayed
34053          * @param {Roo.menu.Menu} this
34054          */
34055         show : true,
34056         /**
34057          * @event hide
34058          * Fires after this menu is hidden
34059          * @param {Roo.menu.Menu} this
34060          */
34061         hide : true,
34062         /**
34063          * @event click
34064          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34065          * @param {Roo.menu.Menu} this
34066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34067          * @param {Roo.EventObject} e
34068          */
34069         click : true,
34070         /**
34071          * @event mouseover
34072          * Fires when the mouse is hovering over this menu
34073          * @param {Roo.menu.Menu} this
34074          * @param {Roo.EventObject} e
34075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34076          */
34077         mouseover : true,
34078         /**
34079          * @event mouseout
34080          * Fires when the mouse exits this menu
34081          * @param {Roo.menu.Menu} this
34082          * @param {Roo.EventObject} e
34083          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34084          */
34085         mouseout : true,
34086         /**
34087          * @event itemclick
34088          * Fires when a menu item contained in this menu is clicked
34089          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34090          * @param {Roo.EventObject} e
34091          */
34092         itemclick: true
34093     });
34094     if (this.registerMenu) {
34095         Roo.menu.MenuMgr.register(this);
34096     }
34097     
34098     var mis = this.items;
34099     this.items = new Roo.util.MixedCollection();
34100     if(mis){
34101         this.add.apply(this, mis);
34102     }
34103 };
34104
34105 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34106     /**
34107      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34108      */
34109     minWidth : 120,
34110     /**
34111      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34112      * for bottom-right shadow (defaults to "sides")
34113      */
34114     shadow : "sides",
34115     /**
34116      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34117      * this menu (defaults to "tl-tr?")
34118      */
34119     subMenuAlign : "tl-tr?",
34120     /**
34121      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34122      * relative to its element of origin (defaults to "tl-bl?")
34123      */
34124     defaultAlign : "tl-bl?",
34125     /**
34126      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34127      */
34128     allowOtherMenus : false,
34129     /**
34130      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34131      */
34132     registerMenu : true,
34133
34134     hidden:true,
34135
34136     // private
34137     render : function(){
34138         if(this.el){
34139             return;
34140         }
34141         var el = this.el = new Roo.Layer({
34142             cls: "x-menu",
34143             shadow:this.shadow,
34144             constrain: false,
34145             parentEl: this.parentEl || document.body,
34146             zindex:15000
34147         });
34148
34149         this.keyNav = new Roo.menu.MenuNav(this);
34150
34151         if(this.plain){
34152             el.addClass("x-menu-plain");
34153         }
34154         if(this.cls){
34155             el.addClass(this.cls);
34156         }
34157         // generic focus element
34158         this.focusEl = el.createChild({
34159             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34160         });
34161         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34162         ul.on("click", this.onClick, this);
34163         ul.on("mouseover", this.onMouseOver, this);
34164         ul.on("mouseout", this.onMouseOut, this);
34165         this.items.each(function(item){
34166             var li = document.createElement("li");
34167             li.className = "x-menu-list-item";
34168             ul.dom.appendChild(li);
34169             item.render(li, this);
34170         }, this);
34171         this.ul = ul;
34172         this.autoWidth();
34173     },
34174
34175     // private
34176     autoWidth : function(){
34177         var el = this.el, ul = this.ul;
34178         if(!el){
34179             return;
34180         }
34181         var w = this.width;
34182         if(w){
34183             el.setWidth(w);
34184         }else if(Roo.isIE){
34185             el.setWidth(this.minWidth);
34186             var t = el.dom.offsetWidth; // force recalc
34187             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34188         }
34189     },
34190
34191     // private
34192     delayAutoWidth : function(){
34193         if(this.rendered){
34194             if(!this.awTask){
34195                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34196             }
34197             this.awTask.delay(20);
34198         }
34199     },
34200
34201     // private
34202     findTargetItem : function(e){
34203         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34204         if(t && t.menuItemId){
34205             return this.items.get(t.menuItemId);
34206         }
34207     },
34208
34209     // private
34210     onClick : function(e){
34211         var t;
34212         if(t = this.findTargetItem(e)){
34213             t.onClick(e);
34214             this.fireEvent("click", this, t, e);
34215         }
34216     },
34217
34218     // private
34219     setActiveItem : function(item, autoExpand){
34220         if(item != this.activeItem){
34221             if(this.activeItem){
34222                 this.activeItem.deactivate();
34223             }
34224             this.activeItem = item;
34225             item.activate(autoExpand);
34226         }else if(autoExpand){
34227             item.expandMenu();
34228         }
34229     },
34230
34231     // private
34232     tryActivate : function(start, step){
34233         var items = this.items;
34234         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34235             var item = items.get(i);
34236             if(!item.disabled && item.canActivate){
34237                 this.setActiveItem(item, false);
34238                 return item;
34239             }
34240         }
34241         return false;
34242     },
34243
34244     // private
34245     onMouseOver : function(e){
34246         var t;
34247         if(t = this.findTargetItem(e)){
34248             if(t.canActivate && !t.disabled){
34249                 this.setActiveItem(t, true);
34250             }
34251         }
34252         this.fireEvent("mouseover", this, e, t);
34253     },
34254
34255     // private
34256     onMouseOut : function(e){
34257         var t;
34258         if(t = this.findTargetItem(e)){
34259             if(t == this.activeItem && t.shouldDeactivate(e)){
34260                 this.activeItem.deactivate();
34261                 delete this.activeItem;
34262             }
34263         }
34264         this.fireEvent("mouseout", this, e, t);
34265     },
34266
34267     /**
34268      * Read-only.  Returns true if the menu is currently displayed, else false.
34269      * @type Boolean
34270      */
34271     isVisible : function(){
34272         return this.el && !this.hidden;
34273     },
34274
34275     /**
34276      * Displays this menu relative to another element
34277      * @param {String/HTMLElement/Roo.Element} element The element to align to
34278      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34279      * the element (defaults to this.defaultAlign)
34280      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34281      */
34282     show : function(el, pos, parentMenu){
34283         this.parentMenu = parentMenu;
34284         if(!this.el){
34285             this.render();
34286         }
34287         this.fireEvent("beforeshow", this);
34288         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34289     },
34290
34291     /**
34292      * Displays this menu at a specific xy position
34293      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34294      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34295      */
34296     showAt : function(xy, parentMenu, /* private: */_e){
34297         this.parentMenu = parentMenu;
34298         if(!this.el){
34299             this.render();
34300         }
34301         if(_e !== false){
34302             this.fireEvent("beforeshow", this);
34303             xy = this.el.adjustForConstraints(xy);
34304         }
34305         this.el.setXY(xy);
34306         this.el.show();
34307         this.hidden = false;
34308         this.focus();
34309         this.fireEvent("show", this);
34310     },
34311
34312     focus : function(){
34313         if(!this.hidden){
34314             this.doFocus.defer(50, this);
34315         }
34316     },
34317
34318     doFocus : function(){
34319         if(!this.hidden){
34320             this.focusEl.focus();
34321         }
34322     },
34323
34324     /**
34325      * Hides this menu and optionally all parent menus
34326      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34327      */
34328     hide : function(deep){
34329         if(this.el && this.isVisible()){
34330             this.fireEvent("beforehide", this);
34331             if(this.activeItem){
34332                 this.activeItem.deactivate();
34333                 this.activeItem = null;
34334             }
34335             this.el.hide();
34336             this.hidden = true;
34337             this.fireEvent("hide", this);
34338         }
34339         if(deep === true && this.parentMenu){
34340             this.parentMenu.hide(true);
34341         }
34342     },
34343
34344     /**
34345      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34346      * Any of the following are valid:
34347      * <ul>
34348      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34349      * <li>An HTMLElement object which will be converted to a menu item</li>
34350      * <li>A menu item config object that will be created as a new menu item</li>
34351      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34352      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34353      * </ul>
34354      * Usage:
34355      * <pre><code>
34356 // Create the menu
34357 var menu = new Roo.menu.Menu();
34358
34359 // Create a menu item to add by reference
34360 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34361
34362 // Add a bunch of items at once using different methods.
34363 // Only the last item added will be returned.
34364 var item = menu.add(
34365     menuItem,                // add existing item by ref
34366     'Dynamic Item',          // new TextItem
34367     '-',                     // new separator
34368     { text: 'Config Item' }  // new item by config
34369 );
34370 </code></pre>
34371      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34372      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34373      */
34374     add : function(){
34375         var a = arguments, l = a.length, item;
34376         for(var i = 0; i < l; i++){
34377             var el = a[i];
34378             if ((typeof(el) == "object") && el.xtype && el.xns) {
34379                 el = Roo.factory(el, Roo.menu);
34380             }
34381             
34382             if(el.render){ // some kind of Item
34383                 item = this.addItem(el);
34384             }else if(typeof el == "string"){ // string
34385                 if(el == "separator" || el == "-"){
34386                     item = this.addSeparator();
34387                 }else{
34388                     item = this.addText(el);
34389                 }
34390             }else if(el.tagName || el.el){ // element
34391                 item = this.addElement(el);
34392             }else if(typeof el == "object"){ // must be menu item config?
34393                 item = this.addMenuItem(el);
34394             }
34395         }
34396         return item;
34397     },
34398
34399     /**
34400      * Returns this menu's underlying {@link Roo.Element} object
34401      * @return {Roo.Element} The element
34402      */
34403     getEl : function(){
34404         if(!this.el){
34405             this.render();
34406         }
34407         return this.el;
34408     },
34409
34410     /**
34411      * Adds a separator bar to the menu
34412      * @return {Roo.menu.Item} The menu item that was added
34413      */
34414     addSeparator : function(){
34415         return this.addItem(new Roo.menu.Separator());
34416     },
34417
34418     /**
34419      * Adds an {@link Roo.Element} object to the menu
34420      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34421      * @return {Roo.menu.Item} The menu item that was added
34422      */
34423     addElement : function(el){
34424         return this.addItem(new Roo.menu.BaseItem(el));
34425     },
34426
34427     /**
34428      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34429      * @param {Roo.menu.Item} item The menu item to add
34430      * @return {Roo.menu.Item} The menu item that was added
34431      */
34432     addItem : function(item){
34433         this.items.add(item);
34434         if(this.ul){
34435             var li = document.createElement("li");
34436             li.className = "x-menu-list-item";
34437             this.ul.dom.appendChild(li);
34438             item.render(li, this);
34439             this.delayAutoWidth();
34440         }
34441         return item;
34442     },
34443
34444     /**
34445      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34446      * @param {Object} config A MenuItem config object
34447      * @return {Roo.menu.Item} The menu item that was added
34448      */
34449     addMenuItem : function(config){
34450         if(!(config instanceof Roo.menu.Item)){
34451             if(typeof config.checked == "boolean"){ // must be check menu item config?
34452                 config = new Roo.menu.CheckItem(config);
34453             }else{
34454                 config = new Roo.menu.Item(config);
34455             }
34456         }
34457         return this.addItem(config);
34458     },
34459
34460     /**
34461      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34462      * @param {String} text The text to display in the menu item
34463      * @return {Roo.menu.Item} The menu item that was added
34464      */
34465     addText : function(text){
34466         return this.addItem(new Roo.menu.TextItem({ text : text }));
34467     },
34468
34469     /**
34470      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34471      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34472      * @param {Roo.menu.Item} item The menu item to add
34473      * @return {Roo.menu.Item} The menu item that was added
34474      */
34475     insert : function(index, item){
34476         this.items.insert(index, item);
34477         if(this.ul){
34478             var li = document.createElement("li");
34479             li.className = "x-menu-list-item";
34480             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34481             item.render(li, this);
34482             this.delayAutoWidth();
34483         }
34484         return item;
34485     },
34486
34487     /**
34488      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34489      * @param {Roo.menu.Item} item The menu item to remove
34490      */
34491     remove : function(item){
34492         this.items.removeKey(item.id);
34493         item.destroy();
34494     },
34495
34496     /**
34497      * Removes and destroys all items in the menu
34498      */
34499     removeAll : function(){
34500         var f;
34501         while(f = this.items.first()){
34502             this.remove(f);
34503         }
34504     }
34505 });
34506
34507 // MenuNav is a private utility class used internally by the Menu
34508 Roo.menu.MenuNav = function(menu){
34509     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34510     this.scope = this.menu = menu;
34511 };
34512
34513 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34514     doRelay : function(e, h){
34515         var k = e.getKey();
34516         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34517             this.menu.tryActivate(0, 1);
34518             return false;
34519         }
34520         return h.call(this.scope || this, e, this.menu);
34521     },
34522
34523     up : function(e, m){
34524         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34525             m.tryActivate(m.items.length-1, -1);
34526         }
34527     },
34528
34529     down : function(e, m){
34530         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34531             m.tryActivate(0, 1);
34532         }
34533     },
34534
34535     right : function(e, m){
34536         if(m.activeItem){
34537             m.activeItem.expandMenu(true);
34538         }
34539     },
34540
34541     left : function(e, m){
34542         m.hide();
34543         if(m.parentMenu && m.parentMenu.activeItem){
34544             m.parentMenu.activeItem.activate();
34545         }
34546     },
34547
34548     enter : function(e, m){
34549         if(m.activeItem){
34550             e.stopPropagation();
34551             m.activeItem.onClick(e);
34552             m.fireEvent("click", this, m.activeItem);
34553             return true;
34554         }
34555     }
34556 });/*
34557  * Based on:
34558  * Ext JS Library 1.1.1
34559  * Copyright(c) 2006-2007, Ext JS, LLC.
34560  *
34561  * Originally Released Under LGPL - original licence link has changed is not relivant.
34562  *
34563  * Fork - LGPL
34564  * <script type="text/javascript">
34565  */
34566  
34567 /**
34568  * @class Roo.menu.MenuMgr
34569  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34570  * @singleton
34571  */
34572 Roo.menu.MenuMgr = function(){
34573    var menus, active, groups = {}, attached = false, lastShow = new Date();
34574
34575    // private - called when first menu is created
34576    function init(){
34577        menus = {};
34578        active = new Roo.util.MixedCollection();
34579        Roo.get(document).addKeyListener(27, function(){
34580            if(active.length > 0){
34581                hideAll();
34582            }
34583        });
34584    }
34585
34586    // private
34587    function hideAll(){
34588        if(active && active.length > 0){
34589            var c = active.clone();
34590            c.each(function(m){
34591                m.hide();
34592            });
34593        }
34594    }
34595
34596    // private
34597    function onHide(m){
34598        active.remove(m);
34599        if(active.length < 1){
34600            Roo.get(document).un("mousedown", onMouseDown);
34601            attached = false;
34602        }
34603    }
34604
34605    // private
34606    function onShow(m){
34607        var last = active.last();
34608        lastShow = new Date();
34609        active.add(m);
34610        if(!attached){
34611            Roo.get(document).on("mousedown", onMouseDown);
34612            attached = true;
34613        }
34614        if(m.parentMenu){
34615           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34616           m.parentMenu.activeChild = m;
34617        }else if(last && last.isVisible()){
34618           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34619        }
34620    }
34621
34622    // private
34623    function onBeforeHide(m){
34624        if(m.activeChild){
34625            m.activeChild.hide();
34626        }
34627        if(m.autoHideTimer){
34628            clearTimeout(m.autoHideTimer);
34629            delete m.autoHideTimer;
34630        }
34631    }
34632
34633    // private
34634    function onBeforeShow(m){
34635        var pm = m.parentMenu;
34636        if(!pm && !m.allowOtherMenus){
34637            hideAll();
34638        }else if(pm && pm.activeChild && active != m){
34639            pm.activeChild.hide();
34640        }
34641    }
34642
34643    // private
34644    function onMouseDown(e){
34645        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34646            hideAll();
34647        }
34648    }
34649
34650    // private
34651    function onBeforeCheck(mi, state){
34652        if(state){
34653            var g = groups[mi.group];
34654            for(var i = 0, l = g.length; i < l; i++){
34655                if(g[i] != mi){
34656                    g[i].setChecked(false);
34657                }
34658            }
34659        }
34660    }
34661
34662    return {
34663
34664        /**
34665         * Hides all menus that are currently visible
34666         */
34667        hideAll : function(){
34668             hideAll();  
34669        },
34670
34671        // private
34672        register : function(menu){
34673            if(!menus){
34674                init();
34675            }
34676            menus[menu.id] = menu;
34677            menu.on("beforehide", onBeforeHide);
34678            menu.on("hide", onHide);
34679            menu.on("beforeshow", onBeforeShow);
34680            menu.on("show", onShow);
34681            var g = menu.group;
34682            if(g && menu.events["checkchange"]){
34683                if(!groups[g]){
34684                    groups[g] = [];
34685                }
34686                groups[g].push(menu);
34687                menu.on("checkchange", onCheck);
34688            }
34689        },
34690
34691         /**
34692          * Returns a {@link Roo.menu.Menu} object
34693          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34694          * be used to generate and return a new Menu instance.
34695          */
34696        get : function(menu){
34697            if(typeof menu == "string"){ // menu id
34698                return menus[menu];
34699            }else if(menu.events){  // menu instance
34700                return menu;
34701            }else if(typeof menu.length == 'number'){ // array of menu items?
34702                return new Roo.menu.Menu({items:menu});
34703            }else{ // otherwise, must be a config
34704                return new Roo.menu.Menu(menu);
34705            }
34706        },
34707
34708        // private
34709        unregister : function(menu){
34710            delete menus[menu.id];
34711            menu.un("beforehide", onBeforeHide);
34712            menu.un("hide", onHide);
34713            menu.un("beforeshow", onBeforeShow);
34714            menu.un("show", onShow);
34715            var g = menu.group;
34716            if(g && menu.events["checkchange"]){
34717                groups[g].remove(menu);
34718                menu.un("checkchange", onCheck);
34719            }
34720        },
34721
34722        // private
34723        registerCheckable : function(menuItem){
34724            var g = menuItem.group;
34725            if(g){
34726                if(!groups[g]){
34727                    groups[g] = [];
34728                }
34729                groups[g].push(menuItem);
34730                menuItem.on("beforecheckchange", onBeforeCheck);
34731            }
34732        },
34733
34734        // private
34735        unregisterCheckable : function(menuItem){
34736            var g = menuItem.group;
34737            if(g){
34738                groups[g].remove(menuItem);
34739                menuItem.un("beforecheckchange", onBeforeCheck);
34740            }
34741        }
34742    };
34743 }();/*
34744  * Based on:
34745  * Ext JS Library 1.1.1
34746  * Copyright(c) 2006-2007, Ext JS, LLC.
34747  *
34748  * Originally Released Under LGPL - original licence link has changed is not relivant.
34749  *
34750  * Fork - LGPL
34751  * <script type="text/javascript">
34752  */
34753  
34754
34755 /**
34756  * @class Roo.menu.BaseItem
34757  * @extends Roo.Component
34758  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34759  * management and base configuration options shared by all menu components.
34760  * @constructor
34761  * Creates a new BaseItem
34762  * @param {Object} config Configuration options
34763  */
34764 Roo.menu.BaseItem = function(config){
34765     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34766
34767     this.addEvents({
34768         /**
34769          * @event click
34770          * Fires when this item is clicked
34771          * @param {Roo.menu.BaseItem} this
34772          * @param {Roo.EventObject} e
34773          */
34774         click: true,
34775         /**
34776          * @event activate
34777          * Fires when this item is activated
34778          * @param {Roo.menu.BaseItem} this
34779          */
34780         activate : true,
34781         /**
34782          * @event deactivate
34783          * Fires when this item is deactivated
34784          * @param {Roo.menu.BaseItem} this
34785          */
34786         deactivate : true
34787     });
34788
34789     if(this.handler){
34790         this.on("click", this.handler, this.scope, true);
34791     }
34792 };
34793
34794 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34795     /**
34796      * @cfg {Function} handler
34797      * A function that will handle the click event of this menu item (defaults to undefined)
34798      */
34799     /**
34800      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34801      */
34802     canActivate : false,
34803     /**
34804      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34805      */
34806     activeClass : "x-menu-item-active",
34807     /**
34808      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34809      */
34810     hideOnClick : true,
34811     /**
34812      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34813      */
34814     hideDelay : 100,
34815
34816     // private
34817     ctype: "Roo.menu.BaseItem",
34818
34819     // private
34820     actionMode : "container",
34821
34822     // private
34823     render : function(container, parentMenu){
34824         this.parentMenu = parentMenu;
34825         Roo.menu.BaseItem.superclass.render.call(this, container);
34826         this.container.menuItemId = this.id;
34827     },
34828
34829     // private
34830     onRender : function(container, position){
34831         this.el = Roo.get(this.el);
34832         container.dom.appendChild(this.el.dom);
34833     },
34834
34835     // private
34836     onClick : function(e){
34837         if(!this.disabled && this.fireEvent("click", this, e) !== false
34838                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34839             this.handleClick(e);
34840         }else{
34841             e.stopEvent();
34842         }
34843     },
34844
34845     // private
34846     activate : function(){
34847         if(this.disabled){
34848             return false;
34849         }
34850         var li = this.container;
34851         li.addClass(this.activeClass);
34852         this.region = li.getRegion().adjust(2, 2, -2, -2);
34853         this.fireEvent("activate", this);
34854         return true;
34855     },
34856
34857     // private
34858     deactivate : function(){
34859         this.container.removeClass(this.activeClass);
34860         this.fireEvent("deactivate", this);
34861     },
34862
34863     // private
34864     shouldDeactivate : function(e){
34865         return !this.region || !this.region.contains(e.getPoint());
34866     },
34867
34868     // private
34869     handleClick : function(e){
34870         if(this.hideOnClick){
34871             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34872         }
34873     },
34874
34875     // private
34876     expandMenu : function(autoActivate){
34877         // do nothing
34878     },
34879
34880     // private
34881     hideMenu : function(){
34882         // do nothing
34883     }
34884 });/*
34885  * Based on:
34886  * Ext JS Library 1.1.1
34887  * Copyright(c) 2006-2007, Ext JS, LLC.
34888  *
34889  * Originally Released Under LGPL - original licence link has changed is not relivant.
34890  *
34891  * Fork - LGPL
34892  * <script type="text/javascript">
34893  */
34894  
34895 /**
34896  * @class Roo.menu.Adapter
34897  * @extends Roo.menu.BaseItem
34898  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34899  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34900  * @constructor
34901  * Creates a new Adapter
34902  * @param {Object} config Configuration options
34903  */
34904 Roo.menu.Adapter = function(component, config){
34905     Roo.menu.Adapter.superclass.constructor.call(this, config);
34906     this.component = component;
34907 };
34908 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34909     // private
34910     canActivate : true,
34911
34912     // private
34913     onRender : function(container, position){
34914         this.component.render(container);
34915         this.el = this.component.getEl();
34916     },
34917
34918     // private
34919     activate : function(){
34920         if(this.disabled){
34921             return false;
34922         }
34923         this.component.focus();
34924         this.fireEvent("activate", this);
34925         return true;
34926     },
34927
34928     // private
34929     deactivate : function(){
34930         this.fireEvent("deactivate", this);
34931     },
34932
34933     // private
34934     disable : function(){
34935         this.component.disable();
34936         Roo.menu.Adapter.superclass.disable.call(this);
34937     },
34938
34939     // private
34940     enable : function(){
34941         this.component.enable();
34942         Roo.menu.Adapter.superclass.enable.call(this);
34943     }
34944 });/*
34945  * Based on:
34946  * Ext JS Library 1.1.1
34947  * Copyright(c) 2006-2007, Ext JS, LLC.
34948  *
34949  * Originally Released Under LGPL - original licence link has changed is not relivant.
34950  *
34951  * Fork - LGPL
34952  * <script type="text/javascript">
34953  */
34954
34955 /**
34956  * @class Roo.menu.TextItem
34957  * @extends Roo.menu.BaseItem
34958  * Adds a static text string to a menu, usually used as either a heading or group separator.
34959  * Note: old style constructor with text is still supported.
34960  * 
34961  * @constructor
34962  * Creates a new TextItem
34963  * @param {Object} cfg Configuration
34964  */
34965 Roo.menu.TextItem = function(cfg){
34966     if (typeof(cfg) == 'string') {
34967         this.text = cfg;
34968     } else {
34969         Roo.apply(this,cfg);
34970     }
34971     
34972     Roo.menu.TextItem.superclass.constructor.call(this);
34973 };
34974
34975 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34976     /**
34977      * @cfg {Boolean} text Text to show on item.
34978      */
34979     text : '',
34980     
34981     /**
34982      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34983      */
34984     hideOnClick : false,
34985     /**
34986      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34987      */
34988     itemCls : "x-menu-text",
34989
34990     // private
34991     onRender : function(){
34992         var s = document.createElement("span");
34993         s.className = this.itemCls;
34994         s.innerHTML = this.text;
34995         this.el = s;
34996         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34997     }
34998 });/*
34999  * Based on:
35000  * Ext JS Library 1.1.1
35001  * Copyright(c) 2006-2007, Ext JS, LLC.
35002  *
35003  * Originally Released Under LGPL - original licence link has changed is not relivant.
35004  *
35005  * Fork - LGPL
35006  * <script type="text/javascript">
35007  */
35008
35009 /**
35010  * @class Roo.menu.Separator
35011  * @extends Roo.menu.BaseItem
35012  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35013  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35014  * @constructor
35015  * @param {Object} config Configuration options
35016  */
35017 Roo.menu.Separator = function(config){
35018     Roo.menu.Separator.superclass.constructor.call(this, config);
35019 };
35020
35021 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35022     /**
35023      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35024      */
35025     itemCls : "x-menu-sep",
35026     /**
35027      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35028      */
35029     hideOnClick : false,
35030
35031     // private
35032     onRender : function(li){
35033         var s = document.createElement("span");
35034         s.className = this.itemCls;
35035         s.innerHTML = "&#160;";
35036         this.el = s;
35037         li.addClass("x-menu-sep-li");
35038         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35039     }
35040 });/*
35041  * Based on:
35042  * Ext JS Library 1.1.1
35043  * Copyright(c) 2006-2007, Ext JS, LLC.
35044  *
35045  * Originally Released Under LGPL - original licence link has changed is not relivant.
35046  *
35047  * Fork - LGPL
35048  * <script type="text/javascript">
35049  */
35050 /**
35051  * @class Roo.menu.Item
35052  * @extends Roo.menu.BaseItem
35053  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35054  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35055  * activation and click handling.
35056  * @constructor
35057  * Creates a new Item
35058  * @param {Object} config Configuration options
35059  */
35060 Roo.menu.Item = function(config){
35061     Roo.menu.Item.superclass.constructor.call(this, config);
35062     if(this.menu){
35063         this.menu = Roo.menu.MenuMgr.get(this.menu);
35064     }
35065 };
35066 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35067     
35068     /**
35069      * @cfg {String} text
35070      * The text to show on the menu item.
35071      */
35072     text: '',
35073      /**
35074      * @cfg {String} HTML to render in menu
35075      * The text to show on the menu item (HTML version).
35076      */
35077     html: '',
35078     /**
35079      * @cfg {String} icon
35080      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35081      */
35082     icon: undefined,
35083     /**
35084      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35085      */
35086     itemCls : "x-menu-item",
35087     /**
35088      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35089      */
35090     canActivate : true,
35091     /**
35092      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35093      */
35094     showDelay: 200,
35095     // doc'd in BaseItem
35096     hideDelay: 200,
35097
35098     // private
35099     ctype: "Roo.menu.Item",
35100     
35101     // private
35102     onRender : function(container, position){
35103         var el = document.createElement("a");
35104         el.hideFocus = true;
35105         el.unselectable = "on";
35106         el.href = this.href || "#";
35107         if(this.hrefTarget){
35108             el.target = this.hrefTarget;
35109         }
35110         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35111         
35112         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35113         
35114         el.innerHTML = String.format(
35115                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35116                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35117         this.el = el;
35118         Roo.menu.Item.superclass.onRender.call(this, container, position);
35119     },
35120
35121     /**
35122      * Sets the text to display in this menu item
35123      * @param {String} text The text to display
35124      * @param {Boolean} isHTML true to indicate text is pure html.
35125      */
35126     setText : function(text, isHTML){
35127         if (isHTML) {
35128             this.html = text;
35129         } else {
35130             this.text = text;
35131             this.html = '';
35132         }
35133         if(this.rendered){
35134             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35135      
35136             this.el.update(String.format(
35137                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35138                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35139             this.parentMenu.autoWidth();
35140         }
35141     },
35142
35143     // private
35144     handleClick : function(e){
35145         if(!this.href){ // if no link defined, stop the event automatically
35146             e.stopEvent();
35147         }
35148         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35149     },
35150
35151     // private
35152     activate : function(autoExpand){
35153         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35154             this.focus();
35155             if(autoExpand){
35156                 this.expandMenu();
35157             }
35158         }
35159         return true;
35160     },
35161
35162     // private
35163     shouldDeactivate : function(e){
35164         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35165             if(this.menu && this.menu.isVisible()){
35166                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35167             }
35168             return true;
35169         }
35170         return false;
35171     },
35172
35173     // private
35174     deactivate : function(){
35175         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35176         this.hideMenu();
35177     },
35178
35179     // private
35180     expandMenu : function(autoActivate){
35181         if(!this.disabled && this.menu){
35182             clearTimeout(this.hideTimer);
35183             delete this.hideTimer;
35184             if(!this.menu.isVisible() && !this.showTimer){
35185                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35186             }else if (this.menu.isVisible() && autoActivate){
35187                 this.menu.tryActivate(0, 1);
35188             }
35189         }
35190     },
35191
35192     // private
35193     deferExpand : function(autoActivate){
35194         delete this.showTimer;
35195         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35196         if(autoActivate){
35197             this.menu.tryActivate(0, 1);
35198         }
35199     },
35200
35201     // private
35202     hideMenu : function(){
35203         clearTimeout(this.showTimer);
35204         delete this.showTimer;
35205         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35206             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35207         }
35208     },
35209
35210     // private
35211     deferHide : function(){
35212         delete this.hideTimer;
35213         this.menu.hide();
35214     }
35215 });/*
35216  * Based on:
35217  * Ext JS Library 1.1.1
35218  * Copyright(c) 2006-2007, Ext JS, LLC.
35219  *
35220  * Originally Released Under LGPL - original licence link has changed is not relivant.
35221  *
35222  * Fork - LGPL
35223  * <script type="text/javascript">
35224  */
35225  
35226 /**
35227  * @class Roo.menu.CheckItem
35228  * @extends Roo.menu.Item
35229  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35230  * @constructor
35231  * Creates a new CheckItem
35232  * @param {Object} config Configuration options
35233  */
35234 Roo.menu.CheckItem = function(config){
35235     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35236     this.addEvents({
35237         /**
35238          * @event beforecheckchange
35239          * Fires before the checked value is set, providing an opportunity to cancel if needed
35240          * @param {Roo.menu.CheckItem} this
35241          * @param {Boolean} checked The new checked value that will be set
35242          */
35243         "beforecheckchange" : true,
35244         /**
35245          * @event checkchange
35246          * Fires after the checked value has been set
35247          * @param {Roo.menu.CheckItem} this
35248          * @param {Boolean} checked The checked value that was set
35249          */
35250         "checkchange" : true
35251     });
35252     if(this.checkHandler){
35253         this.on('checkchange', this.checkHandler, this.scope);
35254     }
35255 };
35256 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35257     /**
35258      * @cfg {String} group
35259      * All check items with the same group name will automatically be grouped into a single-select
35260      * radio button group (defaults to '')
35261      */
35262     /**
35263      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35264      */
35265     itemCls : "x-menu-item x-menu-check-item",
35266     /**
35267      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35268      */
35269     groupClass : "x-menu-group-item",
35270
35271     /**
35272      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35273      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35274      * initialized with checked = true will be rendered as checked.
35275      */
35276     checked: false,
35277
35278     // private
35279     ctype: "Roo.menu.CheckItem",
35280
35281     // private
35282     onRender : function(c){
35283         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35284         if(this.group){
35285             this.el.addClass(this.groupClass);
35286         }
35287         Roo.menu.MenuMgr.registerCheckable(this);
35288         if(this.checked){
35289             this.checked = false;
35290             this.setChecked(true, true);
35291         }
35292     },
35293
35294     // private
35295     destroy : function(){
35296         if(this.rendered){
35297             Roo.menu.MenuMgr.unregisterCheckable(this);
35298         }
35299         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35300     },
35301
35302     /**
35303      * Set the checked state of this item
35304      * @param {Boolean} checked The new checked value
35305      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35306      */
35307     setChecked : function(state, suppressEvent){
35308         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35309             if(this.container){
35310                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35311             }
35312             this.checked = state;
35313             if(suppressEvent !== true){
35314                 this.fireEvent("checkchange", this, state);
35315             }
35316         }
35317     },
35318
35319     // private
35320     handleClick : function(e){
35321        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35322            this.setChecked(!this.checked);
35323        }
35324        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35325     }
35326 });/*
35327  * Based on:
35328  * Ext JS Library 1.1.1
35329  * Copyright(c) 2006-2007, Ext JS, LLC.
35330  *
35331  * Originally Released Under LGPL - original licence link has changed is not relivant.
35332  *
35333  * Fork - LGPL
35334  * <script type="text/javascript">
35335  */
35336  
35337 /**
35338  * @class Roo.menu.DateItem
35339  * @extends Roo.menu.Adapter
35340  * A menu item that wraps the {@link Roo.DatPicker} component.
35341  * @constructor
35342  * Creates a new DateItem
35343  * @param {Object} config Configuration options
35344  */
35345 Roo.menu.DateItem = function(config){
35346     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35347     /** The Roo.DatePicker object @type Roo.DatePicker */
35348     this.picker = this.component;
35349     this.addEvents({select: true});
35350     
35351     this.picker.on("render", function(picker){
35352         picker.getEl().swallowEvent("click");
35353         picker.container.addClass("x-menu-date-item");
35354     });
35355
35356     this.picker.on("select", this.onSelect, this);
35357 };
35358
35359 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35360     // private
35361     onSelect : function(picker, date){
35362         this.fireEvent("select", this, date, picker);
35363         Roo.menu.DateItem.superclass.handleClick.call(this);
35364     }
35365 });/*
35366  * Based on:
35367  * Ext JS Library 1.1.1
35368  * Copyright(c) 2006-2007, Ext JS, LLC.
35369  *
35370  * Originally Released Under LGPL - original licence link has changed is not relivant.
35371  *
35372  * Fork - LGPL
35373  * <script type="text/javascript">
35374  */
35375  
35376 /**
35377  * @class Roo.menu.ColorItem
35378  * @extends Roo.menu.Adapter
35379  * A menu item that wraps the {@link Roo.ColorPalette} component.
35380  * @constructor
35381  * Creates a new ColorItem
35382  * @param {Object} config Configuration options
35383  */
35384 Roo.menu.ColorItem = function(config){
35385     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35386     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35387     this.palette = this.component;
35388     this.relayEvents(this.palette, ["select"]);
35389     if(this.selectHandler){
35390         this.on('select', this.selectHandler, this.scope);
35391     }
35392 };
35393 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35394  * Based on:
35395  * Ext JS Library 1.1.1
35396  * Copyright(c) 2006-2007, Ext JS, LLC.
35397  *
35398  * Originally Released Under LGPL - original licence link has changed is not relivant.
35399  *
35400  * Fork - LGPL
35401  * <script type="text/javascript">
35402  */
35403  
35404
35405 /**
35406  * @class Roo.menu.DateMenu
35407  * @extends Roo.menu.Menu
35408  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35409  * @constructor
35410  * Creates a new DateMenu
35411  * @param {Object} config Configuration options
35412  */
35413 Roo.menu.DateMenu = function(config){
35414     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35415     this.plain = true;
35416     var di = new Roo.menu.DateItem(config);
35417     this.add(di);
35418     /**
35419      * The {@link Roo.DatePicker} instance for this DateMenu
35420      * @type DatePicker
35421      */
35422     this.picker = di.picker;
35423     /**
35424      * @event select
35425      * @param {DatePicker} picker
35426      * @param {Date} date
35427      */
35428     this.relayEvents(di, ["select"]);
35429
35430     this.on('beforeshow', function(){
35431         if(this.picker){
35432             this.picker.hideMonthPicker(true);
35433         }
35434     }, this);
35435 };
35436 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35437     cls:'x-date-menu'
35438 });/*
35439  * Based on:
35440  * Ext JS Library 1.1.1
35441  * Copyright(c) 2006-2007, Ext JS, LLC.
35442  *
35443  * Originally Released Under LGPL - original licence link has changed is not relivant.
35444  *
35445  * Fork - LGPL
35446  * <script type="text/javascript">
35447  */
35448  
35449
35450 /**
35451  * @class Roo.menu.ColorMenu
35452  * @extends Roo.menu.Menu
35453  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35454  * @constructor
35455  * Creates a new ColorMenu
35456  * @param {Object} config Configuration options
35457  */
35458 Roo.menu.ColorMenu = function(config){
35459     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35460     this.plain = true;
35461     var ci = new Roo.menu.ColorItem(config);
35462     this.add(ci);
35463     /**
35464      * The {@link Roo.ColorPalette} instance for this ColorMenu
35465      * @type ColorPalette
35466      */
35467     this.palette = ci.palette;
35468     /**
35469      * @event select
35470      * @param {ColorPalette} palette
35471      * @param {String} color
35472      */
35473     this.relayEvents(ci, ["select"]);
35474 };
35475 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35476  * Based on:
35477  * Ext JS Library 1.1.1
35478  * Copyright(c) 2006-2007, Ext JS, LLC.
35479  *
35480  * Originally Released Under LGPL - original licence link has changed is not relivant.
35481  *
35482  * Fork - LGPL
35483  * <script type="text/javascript">
35484  */
35485  
35486 /**
35487  * @class Roo.form.Field
35488  * @extends Roo.BoxComponent
35489  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35490  * @constructor
35491  * Creates a new Field
35492  * @param {Object} config Configuration options
35493  */
35494 Roo.form.Field = function(config){
35495     Roo.form.Field.superclass.constructor.call(this, config);
35496 };
35497
35498 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35499     /**
35500      * @cfg {String} fieldLabel Label to use when rendering a form.
35501      */
35502        /**
35503      * @cfg {String} qtip Mouse over tip
35504      */
35505      
35506     /**
35507      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35508      */
35509     invalidClass : "x-form-invalid",
35510     /**
35511      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
35512      */
35513     invalidText : "The value in this field is invalid",
35514     /**
35515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35516      */
35517     focusClass : "x-form-focus",
35518     /**
35519      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35520       automatic validation (defaults to "keyup").
35521      */
35522     validationEvent : "keyup",
35523     /**
35524      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35525      */
35526     validateOnBlur : true,
35527     /**
35528      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35529      */
35530     validationDelay : 250,
35531     /**
35532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35533      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35534      */
35535     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35536     /**
35537      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35538      */
35539     fieldClass : "x-form-field",
35540     /**
35541      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35542      *<pre>
35543 Value         Description
35544 -----------   ----------------------------------------------------------------------
35545 qtip          Display a quick tip when the user hovers over the field
35546 title         Display a default browser title attribute popup
35547 under         Add a block div beneath the field containing the error text
35548 side          Add an error icon to the right of the field with a popup on hover
35549 [element id]  Add the error text directly to the innerHTML of the specified element
35550 </pre>
35551      */
35552     msgTarget : 'qtip',
35553     /**
35554      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35555      */
35556     msgFx : 'normal',
35557
35558     /**
35559      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
35560      */
35561     readOnly : false,
35562
35563     /**
35564      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35565      */
35566     disabled : false,
35567
35568     /**
35569      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35570      */
35571     inputType : undefined,
35572     
35573     /**
35574      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
35575          */
35576         tabIndex : undefined,
35577         
35578     // private
35579     isFormField : true,
35580
35581     // private
35582     hasFocus : false,
35583     /**
35584      * @property {Roo.Element} fieldEl
35585      * Element Containing the rendered Field (with label etc.)
35586      */
35587     /**
35588      * @cfg {Mixed} value A value to initialize this field with.
35589      */
35590     value : undefined,
35591
35592     /**
35593      * @cfg {String} name The field's HTML name attribute.
35594      */
35595     /**
35596      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35597      */
35598
35599         // private ??
35600         initComponent : function(){
35601         Roo.form.Field.superclass.initComponent.call(this);
35602         this.addEvents({
35603             /**
35604              * @event focus
35605              * Fires when this field receives input focus.
35606              * @param {Roo.form.Field} this
35607              */
35608             focus : true,
35609             /**
35610              * @event blur
35611              * Fires when this field loses input focus.
35612              * @param {Roo.form.Field} this
35613              */
35614             blur : true,
35615             /**
35616              * @event specialkey
35617              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35618              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35619              * @param {Roo.form.Field} this
35620              * @param {Roo.EventObject} e The event object
35621              */
35622             specialkey : true,
35623             /**
35624              * @event change
35625              * Fires just before the field blurs if the field value has changed.
35626              * @param {Roo.form.Field} this
35627              * @param {Mixed} newValue The new value
35628              * @param {Mixed} oldValue The original value
35629              */
35630             change : true,
35631             /**
35632              * @event invalid
35633              * Fires after the field has been marked as invalid.
35634              * @param {Roo.form.Field} this
35635              * @param {String} msg The validation message
35636              */
35637             invalid : true,
35638             /**
35639              * @event valid
35640              * Fires after the field has been validated with no errors.
35641              * @param {Roo.form.Field} this
35642              */
35643             valid : true,
35644              /**
35645              * @event keyup
35646              * Fires after the key up
35647              * @param {Roo.form.Field} this
35648              * @param {Roo.EventObject}  e The event Object
35649              */
35650             keyup : true
35651         });
35652     },
35653
35654     /**
35655      * Returns the name attribute of the field if available
35656      * @return {String} name The field name
35657      */
35658     getName: function(){
35659          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35660     },
35661
35662     // private
35663     onRender : function(ct, position){
35664         Roo.form.Field.superclass.onRender.call(this, ct, position);
35665         if(!this.el){
35666             var cfg = this.getAutoCreate();
35667             if(!cfg.name){
35668                 cfg.name = this.name || this.id;
35669             }
35670             if(this.inputType){
35671                 cfg.type = this.inputType;
35672             }
35673             this.el = ct.createChild(cfg, position);
35674         }
35675         var type = this.el.dom.type;
35676         if(type){
35677             if(type == 'password'){
35678                 type = 'text';
35679             }
35680             this.el.addClass('x-form-'+type);
35681         }
35682         if(this.readOnly){
35683             this.el.dom.readOnly = true;
35684         }
35685         if(this.tabIndex !== undefined){
35686             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35687         }
35688
35689         this.el.addClass([this.fieldClass, this.cls]);
35690         this.initValue();
35691     },
35692
35693     /**
35694      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35695      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35696      * @return {Roo.form.Field} this
35697      */
35698     applyTo : function(target){
35699         this.allowDomMove = false;
35700         this.el = Roo.get(target);
35701         this.render(this.el.dom.parentNode);
35702         return this;
35703     },
35704
35705     // private
35706     initValue : function(){
35707         if(this.value !== undefined){
35708             this.setValue(this.value);
35709         }else if(this.el.dom.value.length > 0){
35710             this.setValue(this.el.dom.value);
35711         }
35712     },
35713
35714     /**
35715      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35716      */
35717     isDirty : function() {
35718         if(this.disabled) {
35719             return false;
35720         }
35721         return String(this.getValue()) !== String(this.originalValue);
35722     },
35723
35724     // private
35725     afterRender : function(){
35726         Roo.form.Field.superclass.afterRender.call(this);
35727         this.initEvents();
35728     },
35729
35730     // private
35731     fireKey : function(e){
35732         //Roo.log('field ' + e.getKey());
35733         if(e.isNavKeyPress()){
35734             this.fireEvent("specialkey", this, e);
35735         }
35736     },
35737
35738     /**
35739      * Resets the current field value to the originally loaded value and clears any validation messages
35740      */
35741     reset : function(){
35742         this.setValue(this.originalValue);
35743         this.clearInvalid();
35744     },
35745
35746     // private
35747     initEvents : function(){
35748         // safari killled keypress - so keydown is now used..
35749         this.el.on("keydown" , this.fireKey,  this);
35750         this.el.on("focus", this.onFocus,  this);
35751         this.el.on("blur", this.onBlur,  this);
35752         this.el.relayEvent('keyup', this);
35753
35754         // reference to original value for reset
35755         this.originalValue = this.getValue();
35756     },
35757
35758     // private
35759     onFocus : function(){
35760         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35761             this.el.addClass(this.focusClass);
35762         }
35763         if(!this.hasFocus){
35764             this.hasFocus = true;
35765             this.startValue = this.getValue();
35766             this.fireEvent("focus", this);
35767         }
35768     },
35769
35770     beforeBlur : Roo.emptyFn,
35771
35772     // private
35773     onBlur : function(){
35774         this.beforeBlur();
35775         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35776             this.el.removeClass(this.focusClass);
35777         }
35778         this.hasFocus = false;
35779         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35780             this.validate();
35781         }
35782         var v = this.getValue();
35783         if(String(v) !== String(this.startValue)){
35784             this.fireEvent('change', this, v, this.startValue);
35785         }
35786         this.fireEvent("blur", this);
35787     },
35788
35789     /**
35790      * Returns whether or not the field value is currently valid
35791      * @param {Boolean} preventMark True to disable marking the field invalid
35792      * @return {Boolean} True if the value is valid, else false
35793      */
35794     isValid : function(preventMark){
35795         if(this.disabled){
35796             return true;
35797         }
35798         var restore = this.preventMark;
35799         this.preventMark = preventMark === true;
35800         var v = this.validateValue(this.processValue(this.getRawValue()));
35801         this.preventMark = restore;
35802         return v;
35803     },
35804
35805     /**
35806      * Validates the field value
35807      * @return {Boolean} True if the value is valid, else false
35808      */
35809     validate : function(){
35810         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35811             this.clearInvalid();
35812             return true;
35813         }
35814         return false;
35815     },
35816
35817     processValue : function(value){
35818         return value;
35819     },
35820
35821     // private
35822     // Subclasses should provide the validation implementation by overriding this
35823     validateValue : function(value){
35824         return true;
35825     },
35826
35827     /**
35828      * Mark this field as invalid
35829      * @param {String} msg The validation message
35830      */
35831     markInvalid : function(msg){
35832         if(!this.rendered || this.preventMark){ // not rendered
35833             return;
35834         }
35835         this.el.addClass(this.invalidClass);
35836         msg = msg || this.invalidText;
35837         switch(this.msgTarget){
35838             case 'qtip':
35839                 this.el.dom.qtip = msg;
35840                 this.el.dom.qclass = 'x-form-invalid-tip';
35841                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35842                     Roo.QuickTips.enable();
35843                 }
35844                 break;
35845             case 'title':
35846                 this.el.dom.title = msg;
35847                 break;
35848             case 'under':
35849                 if(!this.errorEl){
35850                     var elp = this.el.findParent('.x-form-element', 5, true);
35851                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35852                     this.errorEl.setWidth(elp.getWidth(true)-20);
35853                 }
35854                 this.errorEl.update(msg);
35855                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35856                 break;
35857             case 'side':
35858                 if(!this.errorIcon){
35859                     var elp = this.el.findParent('.x-form-element', 5, true);
35860                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35861                 }
35862                 this.alignErrorIcon();
35863                 this.errorIcon.dom.qtip = msg;
35864                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35865                 this.errorIcon.show();
35866                 this.on('resize', this.alignErrorIcon, this);
35867                 break;
35868             default:
35869                 var t = Roo.getDom(this.msgTarget);
35870                 t.innerHTML = msg;
35871                 t.style.display = this.msgDisplay;
35872                 break;
35873         }
35874         this.fireEvent('invalid', this, msg);
35875     },
35876
35877     // private
35878     alignErrorIcon : function(){
35879         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35880     },
35881
35882     /**
35883      * Clear any invalid styles/messages for this field
35884      */
35885     clearInvalid : function(){
35886         if(!this.rendered || this.preventMark){ // not rendered
35887             return;
35888         }
35889         this.el.removeClass(this.invalidClass);
35890         switch(this.msgTarget){
35891             case 'qtip':
35892                 this.el.dom.qtip = '';
35893                 break;
35894             case 'title':
35895                 this.el.dom.title = '';
35896                 break;
35897             case 'under':
35898                 if(this.errorEl){
35899                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35900                 }
35901                 break;
35902             case 'side':
35903                 if(this.errorIcon){
35904                     this.errorIcon.dom.qtip = '';
35905                     this.errorIcon.hide();
35906                     this.un('resize', this.alignErrorIcon, this);
35907                 }
35908                 break;
35909             default:
35910                 var t = Roo.getDom(this.msgTarget);
35911                 t.innerHTML = '';
35912                 t.style.display = 'none';
35913                 break;
35914         }
35915         this.fireEvent('valid', this);
35916     },
35917
35918     /**
35919      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35920      * @return {Mixed} value The field value
35921      */
35922     getRawValue : function(){
35923         var v = this.el.getValue();
35924         if(v === this.emptyText){
35925             v = '';
35926         }
35927         return v;
35928     },
35929
35930     /**
35931      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35932      * @return {Mixed} value The field value
35933      */
35934     getValue : function(){
35935         var v = this.el.getValue();
35936         if(v === this.emptyText || v === undefined){
35937             v = '';
35938         }
35939         return v;
35940     },
35941
35942     /**
35943      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35944      * @param {Mixed} value The value to set
35945      */
35946     setRawValue : function(v){
35947         var r =  this.el.dom.value = (v === null || v === undefined ? '' : v);
35948         this.shadowNameEl ? (this.shadowNameEl.value =   (v === this.emptyText) ? '' : this.el.dom.value) : false;
35949         
35950     },
35951
35952     /**
35953      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35954      * @param {Mixed} value The value to set
35955      */
35956     setValue : function(v){
35957         this.value = v;
35958         if(this.rendered){
35959             this.el.dom.value = (v === null || v === undefined ? '' : v);
35960             this.shadowNameEl ? (this.shadowNameEl.value = (v === this.emptyText) ? '' : this.el.dom.value) : false;
35961             this.validate();
35962         }
35963     },
35964
35965     adjustSize : function(w, h){
35966         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35967         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35968         return s;
35969     },
35970
35971     adjustWidth : function(tag, w){
35972         tag = tag.toLowerCase();
35973         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35974             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35975                 if(tag == 'input'){
35976                     return w + 2;
35977                 }
35978                 if(tag = 'textarea'){
35979                     return w-2;
35980                 }
35981             }else if(Roo.isOpera){
35982                 if(tag == 'input'){
35983                     return w + 2;
35984                 }
35985                 if(tag = 'textarea'){
35986                     return w-2;
35987                 }
35988             }
35989         }
35990         return w;
35991     }
35992 });
35993
35994
35995 // anything other than normal should be considered experimental
35996 Roo.form.Field.msgFx = {
35997     normal : {
35998         show: function(msgEl, f){
35999             msgEl.setDisplayed('block');
36000         },
36001
36002         hide : function(msgEl, f){
36003             msgEl.setDisplayed(false).update('');
36004         }
36005     },
36006
36007     slide : {
36008         show: function(msgEl, f){
36009             msgEl.slideIn('t', {stopFx:true});
36010         },
36011
36012         hide : function(msgEl, f){
36013             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36014         }
36015     },
36016
36017     slideRight : {
36018         show: function(msgEl, f){
36019             msgEl.fixDisplay();
36020             msgEl.alignTo(f.el, 'tl-tr');
36021             msgEl.slideIn('l', {stopFx:true});
36022         },
36023
36024         hide : function(msgEl, f){
36025             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36026         }
36027     }
36028 };/*
36029  * Based on:
36030  * Ext JS Library 1.1.1
36031  * Copyright(c) 2006-2007, Ext JS, LLC.
36032  *
36033  * Originally Released Under LGPL - original licence link has changed is not relivant.
36034  *
36035  * Fork - LGPL
36036  * <script type="text/javascript">
36037  */
36038  
36039
36040 /**
36041  * @class Roo.form.TextField
36042  * @extends Roo.form.Field
36043  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36044  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36045  * @constructor
36046  * Creates a new TextField
36047  * @param {Object} config Configuration options
36048  */
36049 Roo.form.TextField = function(config){
36050     Roo.form.TextField.superclass.constructor.call(this, config);
36051     this.addEvents({
36052         /**
36053          * @event autosize
36054          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36055          * according to the default logic, but this event provides a hook for the developer to apply additional
36056          * logic at runtime to resize the field if needed.
36057              * @param {Roo.form.Field} this This text field
36058              * @param {Number} width The new field width
36059              */
36060         autosize : true
36061     });
36062 };
36063
36064 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36065     /**
36066      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36067      */
36068     grow : false,
36069     /**
36070      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36071      */
36072     growMin : 30,
36073     /**
36074      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36075      */
36076     growMax : 800,
36077     /**
36078      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36079      */
36080     vtype : null,
36081     /**
36082      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36083      */
36084     maskRe : null,
36085     /**
36086      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36087      */
36088     disableKeyFilter : false,
36089     /**
36090      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36091      */
36092     allowBlank : true,
36093     /**
36094      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36095      */
36096     minLength : 0,
36097     /**
36098      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36099      */
36100     maxLength : Number.MAX_VALUE,
36101     /**
36102      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36103      */
36104     minLengthText : "The minimum length for this field is {0}",
36105     /**
36106      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36107      */
36108     maxLengthText : "The maximum length for this field is {0}",
36109     /**
36110      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36111      */
36112     selectOnFocus : false,
36113     /**
36114      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36115      */
36116     blankText : "This field is required",
36117     /**
36118      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36119      * If available, this function will be called only after the basic validators all return true, and will be passed the
36120      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36121      */
36122     validator : null,
36123     /**
36124      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36125      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36126      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36127      */
36128     regex : null,
36129     /**
36130      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36131      */
36132     regexText : "",
36133     /**
36134      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36135      */
36136     emptyText : null,
36137     /**
36138      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36139      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36140      */
36141     emptyClass : 'x-form-empty-field',
36142
36143     // private
36144     initEvents : function(){
36145         Roo.form.TextField.superclass.initEvents.call(this);
36146         if(this.validationEvent == 'keyup'){
36147             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36148             this.el.on('keyup', this.filterValidation, this);
36149         }
36150         else if(this.validationEvent !== false){
36151             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36152         }
36153         if(this.selectOnFocus || this.emptyText){
36154             this.on("focus", this.preFocus, this);
36155             if(this.emptyText){
36156                 this.on('blur', this.postBlur, this);
36157                 this.applyEmptyText();
36158             }
36159         }
36160         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36161             this.el.on("keypress", this.filterKeys, this);
36162         }
36163         if(this.grow){
36164             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36165             this.el.on("click", this.autoSize,  this);
36166         }
36167     },
36168
36169     processValue : function(value){
36170         if(this.stripCharsRe){
36171             var newValue = value.replace(this.stripCharsRe, '');
36172             if(newValue !== value){
36173                 this.setRawValue(newValue);
36174                 return newValue;
36175             }
36176         }
36177         return value;
36178     },
36179
36180     filterValidation : function(e){
36181         if(!e.isNavKeyPress()){
36182             this.validationTask.delay(this.validationDelay);
36183         }
36184     },
36185
36186     // private
36187     onKeyUp : function(e){
36188         if(!e.isNavKeyPress()){
36189             this.autoSize();
36190         }
36191     },
36192
36193     /**
36194      * Resets the current field value to the originally-loaded value and clears any validation messages.
36195      * Also adds emptyText and emptyClass if the original value was blank.
36196      */
36197     reset : function(){
36198         Roo.form.TextField.superclass.reset.call(this);
36199         this.applyEmptyText();
36200     },
36201
36202     applyEmptyText : function(){
36203         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36204             this.setRawValue(this.emptyText);
36205             this.el.addClass(this.emptyClass);
36206         }
36207     },
36208
36209     // private
36210     preFocus : function(){
36211         if(this.emptyText){
36212             if(this.el.dom.value == this.emptyText){
36213                 this.setRawValue('');
36214             }
36215             this.el.removeClass(this.emptyClass);
36216         }
36217         if(this.selectOnFocus){
36218             this.el.dom.select();
36219         }
36220     },
36221
36222     // private
36223     postBlur : function(){
36224         this.applyEmptyText();
36225     },
36226
36227     // private
36228     filterKeys : function(e){
36229         var k = e.getKey();
36230         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36231             return;
36232         }
36233         var c = e.getCharCode(), cc = String.fromCharCode(c);
36234         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36235             return;
36236         }
36237         if(!this.maskRe.test(cc)){
36238             e.stopEvent();
36239         }
36240     },
36241
36242     setValue : function(v){
36243         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36244             this.el.removeClass(this.emptyClass);
36245         }
36246         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36247         this.applyEmptyText();
36248         this.autoSize();
36249     },
36250
36251     /**
36252      * Validates a value according to the field's validation rules and marks the field as invalid
36253      * if the validation fails
36254      * @param {Mixed} value The value to validate
36255      * @return {Boolean} True if the value is valid, else false
36256      */
36257     validateValue : function(value){
36258         if(value.length < 1 || value === this.emptyText){ // if it's blank
36259              if(this.allowBlank){
36260                 this.clearInvalid();
36261                 return true;
36262              }else{
36263                 this.markInvalid(this.blankText);
36264                 return false;
36265              }
36266         }
36267         if(value.length < this.minLength){
36268             this.markInvalid(String.format(this.minLengthText, this.minLength));
36269             return false;
36270         }
36271         if(value.length > this.maxLength){
36272             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36273             return false;
36274         }
36275         if(this.vtype){
36276             var vt = Roo.form.VTypes;
36277             if(!vt[this.vtype](value, this)){
36278                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36279                 return false;
36280             }
36281         }
36282         if(typeof this.validator == "function"){
36283             var msg = this.validator(value);
36284             if(msg !== true){
36285                 this.markInvalid(msg);
36286                 return false;
36287             }
36288         }
36289         if(this.regex && !this.regex.test(value)){
36290             this.markInvalid(this.regexText);
36291             return false;
36292         }
36293         return true;
36294     },
36295
36296     /**
36297      * Selects text in this field
36298      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36299      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36300      */
36301     selectText : function(start, end){
36302         var v = this.getRawValue();
36303         if(v.length > 0){
36304             start = start === undefined ? 0 : start;
36305             end = end === undefined ? v.length : end;
36306             var d = this.el.dom;
36307             if(d.setSelectionRange){
36308                 d.setSelectionRange(start, end);
36309             }else if(d.createTextRange){
36310                 var range = d.createTextRange();
36311                 range.moveStart("character", start);
36312                 range.moveEnd("character", v.length-end);
36313                 range.select();
36314             }
36315         }
36316     },
36317
36318     /**
36319      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36320      * This only takes effect if grow = true, and fires the autosize event.
36321      */
36322     autoSize : function(){
36323         if(!this.grow || !this.rendered){
36324             return;
36325         }
36326         if(!this.metrics){
36327             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36328         }
36329         var el = this.el;
36330         var v = el.dom.value;
36331         var d = document.createElement('div');
36332         d.appendChild(document.createTextNode(v));
36333         v = d.innerHTML;
36334         d = null;
36335         v += "&#160;";
36336         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36337         this.el.setWidth(w);
36338         this.fireEvent("autosize", this, w);
36339     }
36340 });/*
36341  * Based on:
36342  * Ext JS Library 1.1.1
36343  * Copyright(c) 2006-2007, Ext JS, LLC.
36344  *
36345  * Originally Released Under LGPL - original licence link has changed is not relivant.
36346  *
36347  * Fork - LGPL
36348  * <script type="text/javascript">
36349  */
36350  
36351 /**
36352  * @class Roo.form.Hidden
36353  * @extends Roo.form.TextField
36354  * Simple Hidden element used on forms 
36355  * 
36356  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36357  * 
36358  * @constructor
36359  * Creates a new Hidden form element.
36360  * @param {Object} config Configuration options
36361  */
36362
36363
36364
36365 // easy hidden field...
36366 Roo.form.Hidden = function(config){
36367     Roo.form.Hidden.superclass.constructor.call(this, config);
36368 };
36369   
36370 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36371     fieldLabel:      '',
36372     inputType:      'hidden',
36373     width:          50,
36374     allowBlank:     true,
36375     labelSeparator: '',
36376     hidden:         true,
36377     itemCls :       'x-form-item-display-none'
36378
36379
36380 });
36381
36382
36383 /*
36384  * Based on:
36385  * Ext JS Library 1.1.1
36386  * Copyright(c) 2006-2007, Ext JS, LLC.
36387  *
36388  * Originally Released Under LGPL - original licence link has changed is not relivant.
36389  *
36390  * Fork - LGPL
36391  * <script type="text/javascript">
36392  */
36393  
36394 /**
36395  * @class Roo.form.TriggerField
36396  * @extends Roo.form.TextField
36397  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36398  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36399  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36400  * for which you can provide a custom implementation.  For example:
36401  * <pre><code>
36402 var trigger = new Roo.form.TriggerField();
36403 trigger.onTriggerClick = myTriggerFn;
36404 trigger.applyTo('my-field');
36405 </code></pre>
36406  *
36407  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36408  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36409  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36410  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36411  * @constructor
36412  * Create a new TriggerField.
36413  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36414  * to the base TextField)
36415  */
36416 Roo.form.TriggerField = function(config){
36417     this.mimicing = false;
36418     Roo.form.TriggerField.superclass.constructor.call(this, config);
36419 };
36420
36421 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36422     /**
36423      * @cfg {String} triggerClass A CSS class to apply to the trigger
36424      */
36425     /**
36426      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36427      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36428      */
36429     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36430     /**
36431      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36432      */
36433     hideTrigger:false,
36434
36435     /** @cfg {Boolean} grow @hide */
36436     /** @cfg {Number} growMin @hide */
36437     /** @cfg {Number} growMax @hide */
36438
36439     /**
36440      * @hide 
36441      * @method
36442      */
36443     autoSize: Roo.emptyFn,
36444     // private
36445     monitorTab : true,
36446     // private
36447     deferHeight : true,
36448
36449     
36450     actionMode : 'wrap',
36451     // private
36452     onResize : function(w, h){
36453         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36454         if(typeof w == 'number'){
36455             var x = w - this.trigger.getWidth();
36456             this.el.setWidth(this.adjustWidth('input', x));
36457             this.trigger.setStyle('left', x+'px');
36458         }
36459     },
36460
36461     // private
36462     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36463
36464     // private
36465     getResizeEl : function(){
36466         return this.wrap;
36467     },
36468
36469     // private
36470     getPositionEl : function(){
36471         return this.wrap;
36472     },
36473
36474     // private
36475     alignErrorIcon : function(){
36476         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36477     },
36478
36479     // private
36480     onRender : function(ct, position){
36481         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36482         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36483         this.trigger = this.wrap.createChild(this.triggerConfig ||
36484                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36485         if(this.hideTrigger){
36486             this.trigger.setDisplayed(false);
36487         }
36488         this.initTrigger();
36489         if(!this.width){
36490             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36491         }
36492     },
36493
36494     // private
36495     initTrigger : function(){
36496         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36497         this.trigger.addClassOnOver('x-form-trigger-over');
36498         this.trigger.addClassOnClick('x-form-trigger-click');
36499     },
36500
36501     // private
36502     onDestroy : function(){
36503         if(this.trigger){
36504             this.trigger.removeAllListeners();
36505             this.trigger.remove();
36506         }
36507         if(this.wrap){
36508             this.wrap.remove();
36509         }
36510         Roo.form.TriggerField.superclass.onDestroy.call(this);
36511     },
36512
36513     // private
36514     onFocus : function(){
36515         Roo.form.TriggerField.superclass.onFocus.call(this);
36516         if(!this.mimicing){
36517             this.wrap.addClass('x-trigger-wrap-focus');
36518             this.mimicing = true;
36519             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36520             if(this.monitorTab){
36521                 this.el.on("keydown", this.checkTab, this);
36522             }
36523         }
36524     },
36525
36526     // private
36527     checkTab : function(e){
36528         if(e.getKey() == e.TAB){
36529             this.triggerBlur();
36530         }
36531     },
36532
36533     // private
36534     onBlur : function(){
36535         // do nothing
36536     },
36537
36538     // private
36539     mimicBlur : function(e, t){
36540         if(!this.wrap.contains(t) && this.validateBlur()){
36541             this.triggerBlur();
36542         }
36543     },
36544
36545     // private
36546     triggerBlur : function(){
36547         this.mimicing = false;
36548         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36549         if(this.monitorTab){
36550             this.el.un("keydown", this.checkTab, this);
36551         }
36552         this.wrap.removeClass('x-trigger-wrap-focus');
36553         Roo.form.TriggerField.superclass.onBlur.call(this);
36554     },
36555
36556     // private
36557     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36558     validateBlur : function(e, t){
36559         return true;
36560     },
36561
36562     // private
36563     onDisable : function(){
36564         Roo.form.TriggerField.superclass.onDisable.call(this);
36565         if(this.wrap){
36566             this.wrap.addClass('x-item-disabled');
36567         }
36568     },
36569
36570     // private
36571     onEnable : function(){
36572         Roo.form.TriggerField.superclass.onEnable.call(this);
36573         if(this.wrap){
36574             this.wrap.removeClass('x-item-disabled');
36575         }
36576     },
36577
36578     // private
36579     onShow : function(){
36580         var ae = this.getActionEl();
36581         
36582         if(ae){
36583             ae.dom.style.display = '';
36584             ae.dom.style.visibility = 'visible';
36585         }
36586     },
36587
36588     // private
36589     
36590     onHide : function(){
36591         var ae = this.getActionEl();
36592         ae.dom.style.display = 'none';
36593     },
36594
36595     /**
36596      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36597      * by an implementing function.
36598      * @method
36599      * @param {EventObject} e
36600      */
36601     onTriggerClick : Roo.emptyFn
36602 });
36603
36604 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36605 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36606 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36607 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36608     initComponent : function(){
36609         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36610
36611         this.triggerConfig = {
36612             tag:'span', cls:'x-form-twin-triggers', cn:[
36613             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36614             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36615         ]};
36616     },
36617
36618     getTrigger : function(index){
36619         return this.triggers[index];
36620     },
36621
36622     initTrigger : function(){
36623         var ts = this.trigger.select('.x-form-trigger', true);
36624         this.wrap.setStyle('overflow', 'hidden');
36625         var triggerField = this;
36626         ts.each(function(t, all, index){
36627             t.hide = function(){
36628                 var w = triggerField.wrap.getWidth();
36629                 this.dom.style.display = 'none';
36630                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36631             };
36632             t.show = function(){
36633                 var w = triggerField.wrap.getWidth();
36634                 this.dom.style.display = '';
36635                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36636             };
36637             var triggerIndex = 'Trigger'+(index+1);
36638
36639             if(this['hide'+triggerIndex]){
36640                 t.dom.style.display = 'none';
36641             }
36642             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36643             t.addClassOnOver('x-form-trigger-over');
36644             t.addClassOnClick('x-form-trigger-click');
36645         }, this);
36646         this.triggers = ts.elements;
36647     },
36648
36649     onTrigger1Click : Roo.emptyFn,
36650     onTrigger2Click : Roo.emptyFn
36651 });/*
36652  * Based on:
36653  * Ext JS Library 1.1.1
36654  * Copyright(c) 2006-2007, Ext JS, LLC.
36655  *
36656  * Originally Released Under LGPL - original licence link has changed is not relivant.
36657  *
36658  * Fork - LGPL
36659  * <script type="text/javascript">
36660  */
36661  
36662 /**
36663  * @class Roo.form.TextArea
36664  * @extends Roo.form.TextField
36665  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36666  * support for auto-sizing.
36667  * @constructor
36668  * Creates a new TextArea
36669  * @param {Object} config Configuration options
36670  */
36671 Roo.form.TextArea = function(config){
36672     Roo.form.TextArea.superclass.constructor.call(this, config);
36673     // these are provided exchanges for backwards compat
36674     // minHeight/maxHeight were replaced by growMin/growMax to be
36675     // compatible with TextField growing config values
36676     if(this.minHeight !== undefined){
36677         this.growMin = this.minHeight;
36678     }
36679     if(this.maxHeight !== undefined){
36680         this.growMax = this.maxHeight;
36681     }
36682 };
36683
36684 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36685     /**
36686      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36687      */
36688     growMin : 60,
36689     /**
36690      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36691      */
36692     growMax: 1000,
36693     /**
36694      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36695      * in the field (equivalent to setting overflow: hidden, defaults to false)
36696      */
36697     preventScrollbars: false,
36698     /**
36699      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36700      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36701      */
36702
36703     // private
36704     onRender : function(ct, position){
36705         if(!this.el){
36706             this.defaultAutoCreate = {
36707                 tag: "textarea",
36708                 style:"width:300px;height:60px;",
36709                 autocomplete: "off"
36710             };
36711         }
36712         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36713         if(this.grow){
36714             this.textSizeEl = Roo.DomHelper.append(document.body, {
36715                 tag: "pre", cls: "x-form-grow-sizer"
36716             });
36717             if(this.preventScrollbars){
36718                 this.el.setStyle("overflow", "hidden");
36719             }
36720             this.el.setHeight(this.growMin);
36721         }
36722     },
36723
36724     onDestroy : function(){
36725         if(this.textSizeEl){
36726             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36727         }
36728         Roo.form.TextArea.superclass.onDestroy.call(this);
36729     },
36730
36731     // private
36732     onKeyUp : function(e){
36733         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36734             this.autoSize();
36735         }
36736     },
36737
36738     /**
36739      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36740      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36741      */
36742     autoSize : function(){
36743         if(!this.grow || !this.textSizeEl){
36744             return;
36745         }
36746         var el = this.el;
36747         var v = el.dom.value;
36748         var ts = this.textSizeEl;
36749
36750         ts.innerHTML = '';
36751         ts.appendChild(document.createTextNode(v));
36752         v = ts.innerHTML;
36753
36754         Roo.fly(ts).setWidth(this.el.getWidth());
36755         if(v.length < 1){
36756             v = "&#160;&#160;";
36757         }else{
36758             if(Roo.isIE){
36759                 v = v.replace(/\n/g, '<p>&#160;</p>');
36760             }
36761             v += "&#160;\n&#160;";
36762         }
36763         ts.innerHTML = v;
36764         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36765         if(h != this.lastHeight){
36766             this.lastHeight = h;
36767             this.el.setHeight(h);
36768             this.fireEvent("autosize", this, h);
36769         }
36770     }
36771 });/*
36772  * Based on:
36773  * Ext JS Library 1.1.1
36774  * Copyright(c) 2006-2007, Ext JS, LLC.
36775  *
36776  * Originally Released Under LGPL - original licence link has changed is not relivant.
36777  *
36778  * Fork - LGPL
36779  * <script type="text/javascript">
36780  */
36781  
36782
36783 /**
36784  * @class Roo.form.NumberField
36785  * @extends Roo.form.TextField
36786  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36787  * @constructor
36788  * Creates a new NumberField
36789  * @param {Object} config Configuration options
36790  */
36791 Roo.form.NumberField = function(config){
36792     Roo.form.NumberField.superclass.constructor.call(this, config);
36793 };
36794
36795 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36796     /**
36797      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36798      */
36799     fieldClass: "x-form-field x-form-num-field",
36800     /**
36801      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36802      */
36803     allowDecimals : true,
36804     /**
36805      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36806      */
36807     decimalSeparator : ".",
36808     /**
36809      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36810      */
36811     decimalPrecision : 2,
36812     /**
36813      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36814      */
36815     allowNegative : true,
36816     /**
36817      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36818      */
36819     minValue : Number.NEGATIVE_INFINITY,
36820     /**
36821      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36822      */
36823     maxValue : Number.MAX_VALUE,
36824     /**
36825      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36826      */
36827     minText : "The minimum value for this field is {0}",
36828     /**
36829      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36830      */
36831     maxText : "The maximum value for this field is {0}",
36832     /**
36833      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36834      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36835      */
36836     nanText : "{0} is not a valid number",
36837
36838     // private
36839     initEvents : function(){
36840         Roo.form.NumberField.superclass.initEvents.call(this);
36841         var allowed = "0123456789";
36842         if(this.allowDecimals){
36843             allowed += this.decimalSeparator;
36844         }
36845         if(this.allowNegative){
36846             allowed += "-";
36847         }
36848         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36849         var keyPress = function(e){
36850             var k = e.getKey();
36851             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36852                 return;
36853             }
36854             var c = e.getCharCode();
36855             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36856                 e.stopEvent();
36857             }
36858         };
36859         this.el.on("keypress", keyPress, this);
36860     },
36861
36862     // private
36863     validateValue : function(value){
36864         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36865             return false;
36866         }
36867         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36868              return true;
36869         }
36870         var num = this.parseValue(value);
36871         if(isNaN(num)){
36872             this.markInvalid(String.format(this.nanText, value));
36873             return false;
36874         }
36875         if(num < this.minValue){
36876             this.markInvalid(String.format(this.minText, this.minValue));
36877             return false;
36878         }
36879         if(num > this.maxValue){
36880             this.markInvalid(String.format(this.maxText, this.maxValue));
36881             return false;
36882         }
36883         return true;
36884     },
36885
36886     getValue : function(){
36887         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36888     },
36889
36890     // private
36891     parseValue : function(value){
36892         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36893         return isNaN(value) ? '' : value;
36894     },
36895
36896     // private
36897     fixPrecision : function(value){
36898         var nan = isNaN(value);
36899         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36900             return nan ? '' : value;
36901         }
36902         return parseFloat(value).toFixed(this.decimalPrecision);
36903     },
36904
36905     setValue : function(v){
36906         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36907     },
36908
36909     // private
36910     decimalPrecisionFcn : function(v){
36911         return Math.floor(v);
36912     },
36913
36914     beforeBlur : function(){
36915         var v = this.parseValue(this.getRawValue());
36916         if(v){
36917             this.setValue(this.fixPrecision(v));
36918         }
36919     }
36920 });/*
36921  * Based on:
36922  * Ext JS Library 1.1.1
36923  * Copyright(c) 2006-2007, Ext JS, LLC.
36924  *
36925  * Originally Released Under LGPL - original licence link has changed is not relivant.
36926  *
36927  * Fork - LGPL
36928  * <script type="text/javascript">
36929  */
36930  
36931 /**
36932  * @class Roo.form.DateField
36933  * @extends Roo.form.TriggerField
36934  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36935 * @constructor
36936 * Create a new DateField
36937 * @param {Object} config
36938  */
36939 Roo.form.DateField = function(config){
36940     Roo.form.DateField.superclass.constructor.call(this, config);
36941     
36942       this.addEvents({
36943          
36944         /**
36945          * @event select
36946          * Fires when a date is selected
36947              * @param {Roo.form.DateField} combo This combo box
36948              * @param {Date} date The date selected
36949              */
36950         'select' : true
36951          
36952     });
36953     
36954     
36955     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36956     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36957     this.ddMatch = null;
36958     if(this.disabledDates){
36959         var dd = this.disabledDates;
36960         var re = "(?:";
36961         for(var i = 0; i < dd.length; i++){
36962             re += dd[i];
36963             if(i != dd.length-1) re += "|";
36964         }
36965         this.ddMatch = new RegExp(re + ")");
36966     }
36967 };
36968
36969 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36970     /**
36971      * @cfg {String} format
36972      * The default date format string which can be overriden for localization support.  The format must be
36973      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36974      */
36975     format : "m/d/y",
36976     /**
36977      * @cfg {String} altFormats
36978      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36979      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36980      */
36981     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36982     /**
36983      * @cfg {Array} disabledDays
36984      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36985      */
36986     disabledDays : null,
36987     /**
36988      * @cfg {String} disabledDaysText
36989      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36990      */
36991     disabledDaysText : "Disabled",
36992     /**
36993      * @cfg {Array} disabledDates
36994      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36995      * expression so they are very powerful. Some examples:
36996      * <ul>
36997      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36998      * <li>["03/08", "09/16"] would disable those days for every year</li>
36999      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37000      * <li>["03/../2006"] would disable every day in March 2006</li>
37001      * <li>["^03"] would disable every day in every March</li>
37002      * </ul>
37003      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37004      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37005      */
37006     disabledDates : null,
37007     /**
37008      * @cfg {String} disabledDatesText
37009      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37010      */
37011     disabledDatesText : "Disabled",
37012     /**
37013      * @cfg {Date/String} minValue
37014      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37015      * valid format (defaults to null).
37016      */
37017     minValue : null,
37018     /**
37019      * @cfg {Date/String} maxValue
37020      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37021      * valid format (defaults to null).
37022      */
37023     maxValue : null,
37024     /**
37025      * @cfg {String} minText
37026      * The error text to display when the date in the cell is before minValue (defaults to
37027      * 'The date in this field must be after {minValue}').
37028      */
37029     minText : "The date in this field must be equal to or after {0}",
37030     /**
37031      * @cfg {String} maxText
37032      * The error text to display when the date in the cell is after maxValue (defaults to
37033      * 'The date in this field must be before {maxValue}').
37034      */
37035     maxText : "The date in this field must be equal to or before {0}",
37036     /**
37037      * @cfg {String} invalidText
37038      * The error text to display when the date in the field is invalid (defaults to
37039      * '{value} is not a valid date - it must be in the format {format}').
37040      */
37041     invalidText : "{0} is not a valid date - it must be in the format {1}",
37042     /**
37043      * @cfg {String} triggerClass
37044      * An additional CSS class used to style the trigger button.  The trigger will always get the
37045      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37046      * which displays a calendar icon).
37047      */
37048     triggerClass : 'x-form-date-trigger',
37049     
37050
37051     /**
37052      * @cfg {bool} useIso
37053      * if enabled, then the date field will use a hidden field to store the 
37054      * real value as iso formated date. default (false)
37055      */ 
37056     useIso : false,
37057     /**
37058      * @cfg {String/Object} autoCreate
37059      * A DomHelper element spec, or true for a default element spec (defaults to
37060      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37061      */ 
37062     // private
37063     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37064     
37065     // private
37066     hiddenField: false,
37067     
37068     onRender : function(ct, position)
37069     {
37070         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37071         if (this.useIso) {
37072             this.el.dom.removeAttribute('name'); 
37073             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37074                     'before', true);
37075             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37076             // prevent input submission
37077             this.hiddenName = this.name;
37078         }
37079             
37080             
37081     },
37082     
37083     // private
37084     validateValue : function(value)
37085     {
37086         value = this.formatDate(value);
37087         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37088             return false;
37089         }
37090         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37091              return true;
37092         }
37093         var svalue = value;
37094         value = this.parseDate(value);
37095         if(!value){
37096             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37097             return false;
37098         }
37099         var time = value.getTime();
37100         if(this.minValue && time < this.minValue.getTime()){
37101             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37102             return false;
37103         }
37104         if(this.maxValue && time > this.maxValue.getTime()){
37105             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37106             return false;
37107         }
37108         if(this.disabledDays){
37109             var day = value.getDay();
37110             for(var i = 0; i < this.disabledDays.length; i++) {
37111                 if(day === this.disabledDays[i]){
37112                     this.markInvalid(this.disabledDaysText);
37113                     return false;
37114                 }
37115             }
37116         }
37117         var fvalue = this.formatDate(value);
37118         if(this.ddMatch && this.ddMatch.test(fvalue)){
37119             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37120             return false;
37121         }
37122         return true;
37123     },
37124
37125     // private
37126     // Provides logic to override the default TriggerField.validateBlur which just returns true
37127     validateBlur : function(){
37128         return !this.menu || !this.menu.isVisible();
37129     },
37130
37131     /**
37132      * Returns the current date value of the date field.
37133      * @return {Date} The date value
37134      */
37135     getValue : function(){
37136         
37137         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37138     },
37139
37140     /**
37141      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37142      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37143      * (the default format used is "m/d/y").
37144      * <br />Usage:
37145      * <pre><code>
37146 //All of these calls set the same date value (May 4, 2006)
37147
37148 //Pass a date object:
37149 var dt = new Date('5/4/06');
37150 dateField.setValue(dt);
37151
37152 //Pass a date string (default format):
37153 dateField.setValue('5/4/06');
37154
37155 //Pass a date string (custom format):
37156 dateField.format = 'Y-m-d';
37157 dateField.setValue('2006-5-4');
37158 </code></pre>
37159      * @param {String/Date} date The date or valid date string
37160      */
37161     setValue : function(date){
37162         if (this.hiddenField) {
37163             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37164         }
37165         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37166     },
37167
37168     // private
37169     parseDate : function(value){
37170         if(!value || value instanceof Date){
37171             return value;
37172         }
37173         var v = Date.parseDate(value, this.format);
37174         if(!v && this.altFormats){
37175             if(!this.altFormatsArray){
37176                 this.altFormatsArray = this.altFormats.split("|");
37177             }
37178             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37179                 v = Date.parseDate(value, this.altFormatsArray[i]);
37180             }
37181         }
37182         return v;
37183     },
37184
37185     // private
37186     formatDate : function(date, fmt){
37187         return (!date || !(date instanceof Date)) ?
37188                date : date.dateFormat(fmt || this.format);
37189     },
37190
37191     // private
37192     menuListeners : {
37193         select: function(m, d){
37194             this.setValue(d);
37195             this.fireEvent('select', this, d);
37196         },
37197         show : function(){ // retain focus styling
37198             this.onFocus();
37199         },
37200         hide : function(){
37201             this.focus.defer(10, this);
37202             var ml = this.menuListeners;
37203             this.menu.un("select", ml.select,  this);
37204             this.menu.un("show", ml.show,  this);
37205             this.menu.un("hide", ml.hide,  this);
37206         }
37207     },
37208
37209     // private
37210     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37211     onTriggerClick : function(){
37212         if(this.disabled){
37213             return;
37214         }
37215         if(this.menu == null){
37216             this.menu = new Roo.menu.DateMenu();
37217         }
37218         Roo.apply(this.menu.picker,  {
37219             showClear: this.allowBlank,
37220             minDate : this.minValue,
37221             maxDate : this.maxValue,
37222             disabledDatesRE : this.ddMatch,
37223             disabledDatesText : this.disabledDatesText,
37224             disabledDays : this.disabledDays,
37225             disabledDaysText : this.disabledDaysText,
37226             format : this.format,
37227             minText : String.format(this.minText, this.formatDate(this.minValue)),
37228             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37229         });
37230         this.menu.on(Roo.apply({}, this.menuListeners, {
37231             scope:this
37232         }));
37233         this.menu.picker.setValue(this.getValue() || new Date());
37234         this.menu.show(this.el, "tl-bl?");
37235     },
37236
37237     beforeBlur : function(){
37238         var v = this.parseDate(this.getRawValue());
37239         if(v){
37240             this.setValue(v);
37241         }
37242     }
37243
37244     /** @cfg {Boolean} grow @hide */
37245     /** @cfg {Number} growMin @hide */
37246     /** @cfg {Number} growMax @hide */
37247     /**
37248      * @hide
37249      * @method autoSize
37250      */
37251 });/*
37252  * Based on:
37253  * Ext JS Library 1.1.1
37254  * Copyright(c) 2006-2007, Ext JS, LLC.
37255  *
37256  * Originally Released Under LGPL - original licence link has changed is not relivant.
37257  *
37258  * Fork - LGPL
37259  * <script type="text/javascript">
37260  */
37261  
37262
37263 /**
37264  * @class Roo.form.ComboBox
37265  * @extends Roo.form.TriggerField
37266  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37267  * @constructor
37268  * Create a new ComboBox.
37269  * @param {Object} config Configuration options
37270  */
37271 Roo.form.ComboBox = function(config){
37272     Roo.form.ComboBox.superclass.constructor.call(this, config);
37273     this.addEvents({
37274         /**
37275          * @event expand
37276          * Fires when the dropdown list is expanded
37277              * @param {Roo.form.ComboBox} combo This combo box
37278              */
37279         'expand' : true,
37280         /**
37281          * @event collapse
37282          * Fires when the dropdown list is collapsed
37283              * @param {Roo.form.ComboBox} combo This combo box
37284              */
37285         'collapse' : true,
37286         /**
37287          * @event beforeselect
37288          * Fires before a list item is selected. Return false to cancel the selection.
37289              * @param {Roo.form.ComboBox} combo This combo box
37290              * @param {Roo.data.Record} record The data record returned from the underlying store
37291              * @param {Number} index The index of the selected item in the dropdown list
37292              */
37293         'beforeselect' : true,
37294         /**
37295          * @event select
37296          * Fires when a list item is selected
37297              * @param {Roo.form.ComboBox} combo This combo box
37298              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37299              * @param {Number} index The index of the selected item in the dropdown list
37300              */
37301         'select' : true,
37302         /**
37303          * @event beforequery
37304          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37305          * The event object passed has these properties:
37306              * @param {Roo.form.ComboBox} combo This combo box
37307              * @param {String} query The query
37308              * @param {Boolean} forceAll true to force "all" query
37309              * @param {Boolean} cancel true to cancel the query
37310              * @param {Object} e The query event object
37311              */
37312         'beforequery': true,
37313          /**
37314          * @event add
37315          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37316              * @param {Roo.form.ComboBox} combo This combo box
37317              */
37318         'add' : true,
37319         /**
37320          * @event edit
37321          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37322              * @param {Roo.form.ComboBox} combo This combo box
37323              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37324              */
37325         'edit' : true
37326         
37327         
37328     });
37329     if(this.transform){
37330         this.allowDomMove = false;
37331         var s = Roo.getDom(this.transform);
37332         if(!this.hiddenName){
37333             this.hiddenName = s.name;
37334         }
37335         if(!this.store){
37336             this.mode = 'local';
37337             var d = [], opts = s.options;
37338             for(var i = 0, len = opts.length;i < len; i++){
37339                 var o = opts[i];
37340                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37341                 if(o.selected) {
37342                     this.value = value;
37343                 }
37344                 d.push([value, o.text]);
37345             }
37346             this.store = new Roo.data.SimpleStore({
37347                 'id': 0,
37348                 fields: ['value', 'text'],
37349                 data : d
37350             });
37351             this.valueField = 'value';
37352             this.displayField = 'text';
37353         }
37354         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37355         if(!this.lazyRender){
37356             this.target = true;
37357             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37358             s.parentNode.removeChild(s); // remove it
37359             this.render(this.el.parentNode);
37360         }else{
37361             s.parentNode.removeChild(s); // remove it
37362         }
37363
37364     }
37365     if (this.store) {
37366         this.store = Roo.factory(this.store, Roo.data);
37367     }
37368     
37369     this.selectedIndex = -1;
37370     if(this.mode == 'local'){
37371         if(config.queryDelay === undefined){
37372             this.queryDelay = 10;
37373         }
37374         if(config.minChars === undefined){
37375             this.minChars = 0;
37376         }
37377     }
37378 };
37379
37380 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37381     /**
37382      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37383      */
37384     /**
37385      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37386      * rendering into an Roo.Editor, defaults to false)
37387      */
37388     /**
37389      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37390      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37391      */
37392     /**
37393      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37394      */
37395     /**
37396      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37397      * the dropdown list (defaults to undefined, with no header element)
37398      */
37399
37400      /**
37401      * @cfg {String/Roo.Template} tpl The template to use to render the output
37402      */
37403      
37404     // private
37405     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37406     /**
37407      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37408      */
37409     listWidth: undefined,
37410     /**
37411      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37412      * mode = 'remote' or 'text' if mode = 'local')
37413      */
37414     displayField: undefined,
37415     /**
37416      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37417      * mode = 'remote' or 'value' if mode = 'local'). 
37418      * Note: use of a valueField requires the user make a selection
37419      * in order for a value to be mapped.
37420      */
37421     valueField: undefined,
37422     
37423     
37424     /**
37425      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37426      * field's data value (defaults to the underlying DOM element's name)
37427      */
37428     hiddenName: undefined,
37429     /**
37430      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37431      */
37432     listClass: '',
37433     /**
37434      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37435      */
37436     selectedClass: 'x-combo-selected',
37437     /**
37438      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37439      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37440      * which displays a downward arrow icon).
37441      */
37442     triggerClass : 'x-form-arrow-trigger',
37443     /**
37444      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37445      */
37446     shadow:'sides',
37447     /**
37448      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37449      * anchor positions (defaults to 'tl-bl')
37450      */
37451     listAlign: 'tl-bl?',
37452     /**
37453      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37454      */
37455     maxHeight: 300,
37456     /**
37457      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37458      * query specified by the allQuery config option (defaults to 'query')
37459      */
37460     triggerAction: 'query',
37461     /**
37462      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37463      * (defaults to 4, does not apply if editable = false)
37464      */
37465     minChars : 4,
37466     /**
37467      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37468      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37469      */
37470     typeAhead: false,
37471     /**
37472      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37473      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37474      */
37475     queryDelay: 500,
37476     /**
37477      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37478      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37479      */
37480     pageSize: 0,
37481     /**
37482      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37483      * when editable = true (defaults to false)
37484      */
37485     selectOnFocus:false,
37486     /**
37487      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37488      */
37489     queryParam: 'query',
37490     /**
37491      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37492      * when mode = 'remote' (defaults to 'Loading...')
37493      */
37494     loadingText: 'Loading...',
37495     /**
37496      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37497      */
37498     resizable: false,
37499     /**
37500      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37501      */
37502     handleHeight : 8,
37503     /**
37504      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37505      * traditional select (defaults to true)
37506      */
37507     editable: true,
37508     /**
37509      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37510      */
37511     allQuery: '',
37512     /**
37513      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37514      */
37515     mode: 'remote',
37516     /**
37517      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37518      * listWidth has a higher value)
37519      */
37520     minListWidth : 70,
37521     /**
37522      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37523      * allow the user to set arbitrary text into the field (defaults to false)
37524      */
37525     forceSelection:false,
37526     /**
37527      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37528      * if typeAhead = true (defaults to 250)
37529      */
37530     typeAheadDelay : 250,
37531     /**
37532      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37533      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37534      */
37535     valueNotFoundText : undefined,
37536     /**
37537      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37538      */
37539     blockFocus : false,
37540     
37541     /**
37542      * @cfg {Boolean} disableClear Disable showing of clear button.
37543      */
37544     disableClear : false,
37545     /**
37546      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37547      */
37548     alwaysQuery : false,
37549     
37550     //private
37551     addicon : false,
37552     editicon: false,
37553     
37554     // element that contains real text value.. (when hidden is used..)
37555     shadowNameEl : undefined,
37556     
37557     // private
37558     onRender : function(ct, position){
37559         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37560         if(this.hiddenName){
37561             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37562                     'before', true);
37563             this.hiddenField.value =
37564                 this.hiddenValue !== undefined ? this.hiddenValue :
37565                 this.value !== undefined ? this.value : '';
37566
37567             // prevent input submission
37568             //if (this.hiddenName == this.name) { 
37569             this.el.dom.removeAttribute('name');
37570             this.shadowNameEl = this.el.insertSibling({tag:'input', type:'hidden', name: this.name}, 'before', true);
37571             
37572             
37573             //}
37574         }
37575         if(Roo.isGecko){
37576             this.el.dom.setAttribute('autocomplete', 'off');
37577         }
37578
37579         var cls = 'x-combo-list';
37580
37581         this.list = new Roo.Layer({
37582             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37583         });
37584
37585         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37586         this.list.setWidth(lw);
37587         this.list.swallowEvent('mousewheel');
37588         this.assetHeight = 0;
37589
37590         if(this.title){
37591             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37592             this.assetHeight += this.header.getHeight();
37593         }
37594
37595         this.innerList = this.list.createChild({cls:cls+'-inner'});
37596         this.innerList.on('mouseover', this.onViewOver, this);
37597         this.innerList.on('mousemove', this.onViewMove, this);
37598         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37599         
37600         if(this.allowBlank && !this.pageSize && !this.disableClear){
37601             this.footer = this.list.createChild({cls:cls+'-ft'});
37602             this.pageTb = new Roo.Toolbar(this.footer);
37603            
37604         }
37605         if(this.pageSize){
37606             this.footer = this.list.createChild({cls:cls+'-ft'});
37607             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37608                     {pageSize: this.pageSize});
37609             
37610         }
37611         
37612         if (this.pageTb && this.allowBlank && !this.disableClear) {
37613             var _this = this;
37614             this.pageTb.add(new Roo.Toolbar.Fill(), {
37615                 cls: 'x-btn-icon x-btn-clear',
37616                 text: '&#160;',
37617                 handler: function()
37618                 {
37619                     _this.collapse();
37620                     _this.clearValue();
37621                     _this.onSelect(false, -1);
37622                 }
37623             });
37624         }
37625         if (this.footer) {
37626             this.assetHeight += this.footer.getHeight();
37627         }
37628         
37629
37630         if(!this.tpl){
37631             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37632         }
37633
37634         this.view = new Roo.View(this.innerList, this.tpl, {
37635             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37636         });
37637
37638         this.view.on('click', this.onViewClick, this);
37639
37640         this.store.on('beforeload', this.onBeforeLoad, this);
37641         this.store.on('load', this.onLoad, this);
37642         this.store.on('loadexception', this.collapse, this);
37643
37644         if(this.resizable){
37645             this.resizer = new Roo.Resizable(this.list,  {
37646                pinned:true, handles:'se'
37647             });
37648             this.resizer.on('resize', function(r, w, h){
37649                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37650                 this.listWidth = w;
37651                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37652                 this.restrictHeight();
37653             }, this);
37654             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37655         }
37656         if(!this.editable){
37657             this.editable = true;
37658             this.setEditable(false);
37659         }  
37660         
37661         
37662         if (typeof(this.events.add.listeners) != 'undefined') {
37663             
37664             this.addicon = this.wrap.createChild(
37665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37666        
37667             this.addicon.on('click', function(e) {
37668                 this.fireEvent('add', this);
37669             }, this);
37670         }
37671         if (typeof(this.events.edit.listeners) != 'undefined') {
37672             
37673             this.editicon = this.wrap.createChild(
37674                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37675             if (this.addicon) {
37676                 this.editicon.setStyle('margin-left', '40px');
37677             }
37678             this.editicon.on('click', function(e) {
37679                 
37680                 // we fire even  if inothing is selected..
37681                 this.fireEvent('edit', this, this.lastData );
37682                 
37683             }, this);
37684         }
37685         
37686         
37687         
37688     },
37689
37690     // private
37691     initEvents : function(){
37692         Roo.form.ComboBox.superclass.initEvents.call(this);
37693
37694         this.keyNav = new Roo.KeyNav(this.el, {
37695             "up" : function(e){
37696                 this.inKeyMode = true;
37697                 this.selectPrev();
37698             },
37699
37700             "down" : function(e){
37701                 if(!this.isExpanded()){
37702                     this.onTriggerClick();
37703                 }else{
37704                     this.inKeyMode = true;
37705                     this.selectNext();
37706                 }
37707             },
37708
37709             "enter" : function(e){
37710                 this.onViewClick();
37711                 //return true;
37712             },
37713
37714             "esc" : function(e){
37715                 this.collapse();
37716             },
37717
37718             "tab" : function(e){
37719                 this.onViewClick(false);
37720                 this.fireEvent("specialkey", this, e);
37721                 return true;
37722             },
37723
37724             scope : this,
37725
37726             doRelay : function(foo, bar, hname){
37727                 if(hname == 'down' || this.scope.isExpanded()){
37728                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37729                 }
37730                 return true;
37731             },
37732
37733             forceKeyDown: true
37734         });
37735         this.queryDelay = Math.max(this.queryDelay || 10,
37736                 this.mode == 'local' ? 10 : 250);
37737         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37738         if(this.typeAhead){
37739             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37740         }
37741         if(this.editable !== false){
37742             this.el.on("keyup", this.onKeyUp, this);
37743         }
37744         if(this.forceSelection){
37745             this.on('blur', this.doForce, this);
37746         }
37747     },
37748
37749     onDestroy : function(){
37750         if(this.view){
37751             this.view.setStore(null);
37752             this.view.el.removeAllListeners();
37753             this.view.el.remove();
37754             this.view.purgeListeners();
37755         }
37756         if(this.list){
37757             this.list.destroy();
37758         }
37759         if(this.store){
37760             this.store.un('beforeload', this.onBeforeLoad, this);
37761             this.store.un('load', this.onLoad, this);
37762             this.store.un('loadexception', this.collapse, this);
37763         }
37764         Roo.form.ComboBox.superclass.onDestroy.call(this);
37765     },
37766
37767     // private
37768     fireKey : function(e){
37769         if(e.isNavKeyPress() && !this.list.isVisible()){
37770             this.fireEvent("specialkey", this, e);
37771         }
37772     },
37773
37774     // private
37775     onResize: function(w, h){
37776         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37777         
37778         if(typeof w != 'number'){
37779             // we do not handle it!?!?
37780             return;
37781         }
37782         var tw = this.trigger.getWidth();
37783         tw += this.addicon ? this.addicon.getWidth() : 0;
37784         tw += this.editicon ? this.editicon.getWidth() : 0;
37785         var x = w - tw;
37786         this.el.setWidth( this.adjustWidth('input', x));
37787             
37788         this.trigger.setStyle('left', x+'px');
37789         
37790         if(this.list && this.listWidth === undefined){
37791             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37792             this.list.setWidth(lw);
37793             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37794         }
37795         
37796     
37797         
37798     },
37799
37800     /**
37801      * Allow or prevent the user from directly editing the field text.  If false is passed,
37802      * the user will only be able to select from the items defined in the dropdown list.  This method
37803      * is the runtime equivalent of setting the 'editable' config option at config time.
37804      * @param {Boolean} value True to allow the user to directly edit the field text
37805      */
37806     setEditable : function(value){
37807         if(value == this.editable){
37808             return;
37809         }
37810         this.editable = value;
37811         if(!value){
37812             this.el.dom.setAttribute('readOnly', true);
37813             this.el.on('mousedown', this.onTriggerClick,  this);
37814             this.el.addClass('x-combo-noedit');
37815         }else{
37816             this.el.dom.setAttribute('readOnly', false);
37817             this.el.un('mousedown', this.onTriggerClick,  this);
37818             this.el.removeClass('x-combo-noedit');
37819         }
37820     },
37821
37822     // private
37823     onBeforeLoad : function(){
37824         if(!this.hasFocus){
37825             return;
37826         }
37827         this.innerList.update(this.loadingText ?
37828                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37829         this.restrictHeight();
37830         this.selectedIndex = -1;
37831     },
37832
37833     // private
37834     onLoad : function(){
37835         if(!this.hasFocus){
37836             return;
37837         }
37838         if(this.store.getCount() > 0){
37839             this.expand();
37840             this.restrictHeight();
37841             if(this.lastQuery == this.allQuery){
37842                 if(this.editable){
37843                     this.el.dom.select();
37844                 }
37845                 if(!this.selectByValue(this.value, true)){
37846                     this.select(0, true);
37847                 }
37848             }else{
37849                 this.selectNext();
37850                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37851                     this.taTask.delay(this.typeAheadDelay);
37852                 }
37853             }
37854         }else{
37855             this.onEmptyResults();
37856         }
37857         //this.el.focus();
37858     },
37859
37860     // private
37861     onTypeAhead : function(){
37862         if(this.store.getCount() > 0){
37863             var r = this.store.getAt(0);
37864             var newValue = r.data[this.displayField];
37865             var len = newValue.length;
37866             var selStart = this.getRawValue().length;
37867             if(selStart != len){
37868                 this.setRawValue(newValue);
37869                 this.selectText(selStart, newValue.length);
37870             }
37871         }
37872     },
37873
37874     // private
37875     onSelect : function(record, index){
37876         if(this.fireEvent('beforeselect', this, record, index) !== false){
37877             this.setFromData(index > -1 ? record.data : false);
37878             this.collapse();
37879             this.fireEvent('select', this, record, index);
37880         }
37881     },
37882
37883     /**
37884      * Returns the currently selected field value or empty string if no value is set.
37885      * @return {String} value The selected value
37886      */
37887     getValue : function(){
37888         if(this.valueField){
37889             return typeof this.value != 'undefined' ? this.value : '';
37890         }else{
37891             return Roo.form.ComboBox.superclass.getValue.call(this);
37892         }
37893     },
37894
37895     /**
37896      * Clears any text/value currently set in the field
37897      */
37898     clearValue : function(){
37899         if(this.hiddenField){
37900             this.hiddenField.value = '';
37901         }
37902         this.value = '';
37903         this.setRawValue('');
37904         this.lastSelectionText = '';
37905         this.applyEmptyText();
37906     },
37907
37908     /**
37909      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37910      * will be displayed in the field.  If the value does not match the data value of an existing item,
37911      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37912      * Otherwise the field will be blank (although the value will still be set).
37913      * @param {String} value The value to match
37914      */
37915     setValue : function(v){
37916         var text = v;
37917         if(this.valueField){
37918             var r = this.findRecord(this.valueField, v);
37919             if(r){
37920                 text = r.data[this.displayField];
37921             }else if(this.valueNotFoundText !== undefined){
37922                 text = this.valueNotFoundText;
37923             }
37924         }
37925         this.lastSelectionText = text;
37926         if(this.hiddenField){
37927             this.hiddenField.value = v;
37928         }
37929         Roo.form.ComboBox.superclass.setValue.call(this, text);
37930         this.value = v;
37931     },
37932     /**
37933      * @property {Object} the last set data for the element
37934      */
37935     
37936     lastData : false,
37937     /**
37938      * Sets the value of the field based on a object which is related to the record format for the store.
37939      * @param {Object} value the value to set as. or false on reset?
37940      */
37941     setFromData : function(o){
37942         var dv = ''; // display value
37943         var vv = ''; // value value..
37944         this.lastData = o;
37945         if (this.displayField) {
37946             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37947         } else {
37948             // this is an error condition!!!
37949             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37950         }
37951         
37952         if(this.valueField){
37953             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37954         }
37955         if(this.hiddenField){
37956             this.hiddenField.value = vv;
37957             
37958             this.lastSelectionText = dv;
37959             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37960             this.value = vv;
37961             return;
37962         }
37963         // no hidden field.. - we store the value in 'value', but still display
37964         // display field!!!!
37965         this.lastSelectionText = dv;
37966         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37967         this.value = vv;
37968         
37969         
37970     },
37971     // private
37972     reset : function(){
37973         // overridden so that last data is reset..
37974         this.setValue(this.originalValue);
37975         this.clearInvalid();
37976         this.lastData = false;
37977     },
37978     // private
37979     findRecord : function(prop, value){
37980         var record;
37981         if(this.store.getCount() > 0){
37982             this.store.each(function(r){
37983                 if(r.data[prop] == value){
37984                     record = r;
37985                     return false;
37986                 }
37987                 return true;
37988             });
37989         }
37990         return record;
37991     },
37992     
37993     getName: function()
37994     {
37995         // returns hidden if it's set..
37996         if (!this.rendered) {return ''};
37997         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37998         
37999     },
38000     // private
38001     onViewMove : function(e, t){
38002         this.inKeyMode = false;
38003     },
38004
38005     // private
38006     onViewOver : function(e, t){
38007         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38008             return;
38009         }
38010         var item = this.view.findItemFromChild(t);
38011         if(item){
38012             var index = this.view.indexOf(item);
38013             this.select(index, false);
38014         }
38015     },
38016
38017     // private
38018     onViewClick : function(doFocus)
38019     {
38020         var index = this.view.getSelectedIndexes()[0];
38021         var r = this.store.getAt(index);
38022         if(r){
38023             this.onSelect(r, index);
38024         }
38025         if(doFocus !== false && !this.blockFocus){
38026             this.el.focus();
38027         }
38028     },
38029
38030     // private
38031     restrictHeight : function(){
38032         this.innerList.dom.style.height = '';
38033         var inner = this.innerList.dom;
38034         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38035         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38036         this.list.beginUpdate();
38037         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38038         this.list.alignTo(this.el, this.listAlign);
38039         this.list.endUpdate();
38040     },
38041
38042     // private
38043     onEmptyResults : function(){
38044         this.collapse();
38045     },
38046
38047     /**
38048      * Returns true if the dropdown list is expanded, else false.
38049      */
38050     isExpanded : function(){
38051         return this.list.isVisible();
38052     },
38053
38054     /**
38055      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38056      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38057      * @param {String} value The data value of the item to select
38058      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38059      * selected item if it is not currently in view (defaults to true)
38060      * @return {Boolean} True if the value matched an item in the list, else false
38061      */
38062     selectByValue : function(v, scrollIntoView){
38063         if(v !== undefined && v !== null){
38064             var r = this.findRecord(this.valueField || this.displayField, v);
38065             if(r){
38066                 this.select(this.store.indexOf(r), scrollIntoView);
38067                 return true;
38068             }
38069         }
38070         return false;
38071     },
38072
38073     /**
38074      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38075      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38076      * @param {Number} index The zero-based index of the list item to select
38077      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38078      * selected item if it is not currently in view (defaults to true)
38079      */
38080     select : function(index, scrollIntoView){
38081         this.selectedIndex = index;
38082         this.view.select(index);
38083         if(scrollIntoView !== false){
38084             var el = this.view.getNode(index);
38085             if(el){
38086                 this.innerList.scrollChildIntoView(el, false);
38087             }
38088         }
38089     },
38090
38091     // private
38092     selectNext : function(){
38093         var ct = this.store.getCount();
38094         if(ct > 0){
38095             if(this.selectedIndex == -1){
38096                 this.select(0);
38097             }else if(this.selectedIndex < ct-1){
38098                 this.select(this.selectedIndex+1);
38099             }
38100         }
38101     },
38102
38103     // private
38104     selectPrev : function(){
38105         var ct = this.store.getCount();
38106         if(ct > 0){
38107             if(this.selectedIndex == -1){
38108                 this.select(0);
38109             }else if(this.selectedIndex != 0){
38110                 this.select(this.selectedIndex-1);
38111             }
38112         }
38113     },
38114
38115     // private
38116     onKeyUp : function(e){
38117         if(this.editable !== false && !e.isSpecialKey()){
38118             this.lastKey = e.getKey();
38119             this.dqTask.delay(this.queryDelay);
38120         }
38121     },
38122
38123     // private
38124     validateBlur : function(){
38125         return !this.list || !this.list.isVisible();   
38126     },
38127
38128     // private
38129     initQuery : function(){
38130         this.doQuery(this.getRawValue());
38131     },
38132
38133     // private
38134     doForce : function(){
38135         if(this.el.dom.value.length > 0){
38136             this.el.dom.value =
38137                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38138             this.shadowNameEl ? (this.shadowNameEl.dom.value = this.el.dom.value) : false;
38139             this.applyEmptyText();
38140         }
38141     },
38142
38143     /**
38144      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38145      * query allowing the query action to be canceled if needed.
38146      * @param {String} query The SQL query to execute
38147      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38148      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38149      * saved in the current store (defaults to false)
38150      */
38151     doQuery : function(q, forceAll){
38152         if(q === undefined || q === null){
38153             q = '';
38154         }
38155         var qe = {
38156             query: q,
38157             forceAll: forceAll,
38158             combo: this,
38159             cancel:false
38160         };
38161         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38162             return false;
38163         }
38164         q = qe.query;
38165         forceAll = qe.forceAll;
38166         if(forceAll === true || (q.length >= this.minChars)){
38167             if(this.lastQuery != q || this.alwaysQuery){
38168                 this.lastQuery = q;
38169                 if(this.mode == 'local'){
38170                     this.selectedIndex = -1;
38171                     if(forceAll){
38172                         this.store.clearFilter();
38173                     }else{
38174                         this.store.filter(this.displayField, q);
38175                     }
38176                     this.onLoad();
38177                 }else{
38178                     this.store.baseParams[this.queryParam] = q;
38179                     this.store.load({
38180                         params: this.getParams(q)
38181                     });
38182                     this.expand();
38183                 }
38184             }else{
38185                 this.selectedIndex = -1;
38186                 this.onLoad();   
38187             }
38188         }
38189     },
38190
38191     // private
38192     getParams : function(q){
38193         var p = {};
38194         //p[this.queryParam] = q;
38195         if(this.pageSize){
38196             p.start = 0;
38197             p.limit = this.pageSize;
38198         }
38199         return p;
38200     },
38201
38202     /**
38203      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38204      */
38205     collapse : function(){
38206         if(!this.isExpanded()){
38207             return;
38208         }
38209         this.list.hide();
38210         Roo.get(document).un('mousedown', this.collapseIf, this);
38211         Roo.get(document).un('mousewheel', this.collapseIf, this);
38212         if (!this.editable) {
38213             Roo.get(document).un('keydown', this.listKeyPress, this);
38214         }
38215         this.fireEvent('collapse', this);
38216     },
38217
38218     // private
38219     collapseIf : function(e){
38220         if(!e.within(this.wrap) && !e.within(this.list)){
38221             this.collapse();
38222         }
38223     },
38224
38225     /**
38226      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38227      */
38228     expand : function(){
38229         if(this.isExpanded() || !this.hasFocus){
38230             return;
38231         }
38232         this.list.alignTo(this.el, this.listAlign);
38233         this.list.show();
38234         Roo.get(document).on('mousedown', this.collapseIf, this);
38235         Roo.get(document).on('mousewheel', this.collapseIf, this);
38236         if (!this.editable) {
38237             Roo.get(document).on('keydown', this.listKeyPress, this);
38238         }
38239         
38240         this.fireEvent('expand', this);
38241     },
38242
38243     // private
38244     // Implements the default empty TriggerField.onTriggerClick function
38245     onTriggerClick : function(){
38246         if(this.disabled){
38247             return;
38248         }
38249         if(this.isExpanded()){
38250             this.collapse();
38251             if (!this.blockFocus) {
38252                 this.el.focus();
38253             }
38254             
38255         }else {
38256             this.hasFocus = true;
38257             if(this.triggerAction == 'all') {
38258                 this.doQuery(this.allQuery, true);
38259             } else {
38260                 this.doQuery(this.getRawValue());
38261             }
38262             if (!this.blockFocus) {
38263                 this.el.focus();
38264             }
38265         }
38266     },
38267     listKeyPress : function(e)
38268     {
38269         //Roo.log('listkeypress');
38270         // scroll to first matching element based on key pres..
38271         if (e.isSpecialKey()) {
38272             return false;
38273         }
38274         var k = String.fromCharCode(e.getKey()).toUpperCase();
38275         //Roo.log(k);
38276         var match  = false;
38277         var csel = this.view.getSelectedNodes();
38278         var cselitem = false;
38279         if (csel.length) {
38280             var ix = this.view.indexOf(csel[0]);
38281             cselitem  = this.store.getAt(ix);
38282             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38283                 cselitem = false;
38284             }
38285             
38286         }
38287         
38288         this.store.each(function(v) { 
38289             if (cselitem) {
38290                 // start at existing selection.
38291                 if (cselitem.id == v.id) {
38292                     cselitem = false;
38293                 }
38294                 return;
38295             }
38296                 
38297             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38298                 match = this.store.indexOf(v);
38299                 return false;
38300             }
38301         }, this);
38302         
38303         if (match === false) {
38304             return true; // no more action?
38305         }
38306         // scroll to?
38307         this.view.select(match);
38308         var sn = Roo.get(this.view.getSelectedNodes()[0])
38309         sn.scrollIntoView(sn.dom.parentNode, false);
38310     }
38311
38312     /** 
38313     * @cfg {Boolean} grow 
38314     * @hide 
38315     */
38316     /** 
38317     * @cfg {Number} growMin 
38318     * @hide 
38319     */
38320     /** 
38321     * @cfg {Number} growMax 
38322     * @hide 
38323     */
38324     /**
38325      * @hide
38326      * @method autoSize
38327      */
38328 });/*
38329  * Based on:
38330  * Ext JS Library 1.1.1
38331  * Copyright(c) 2006-2007, Ext JS, LLC.
38332  *
38333  * Originally Released Under LGPL - original licence link has changed is not relivant.
38334  *
38335  * Fork - LGPL
38336  * <script type="text/javascript">
38337  */
38338 /**
38339  * @class Roo.form.Checkbox
38340  * @extends Roo.form.Field
38341  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38342  * @constructor
38343  * Creates a new Checkbox
38344  * @param {Object} config Configuration options
38345  */
38346 Roo.form.Checkbox = function(config){
38347     Roo.form.Checkbox.superclass.constructor.call(this, config);
38348     this.addEvents({
38349         /**
38350          * @event check
38351          * Fires when the checkbox is checked or unchecked.
38352              * @param {Roo.form.Checkbox} this This checkbox
38353              * @param {Boolean} checked The new checked value
38354              */
38355         check : true
38356     });
38357 };
38358
38359 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38360     /**
38361      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38362      */
38363     focusClass : undefined,
38364     /**
38365      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38366      */
38367     fieldClass: "x-form-field",
38368     /**
38369      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38370      */
38371     checked: false,
38372     /**
38373      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38374      * {tag: "input", type: "checkbox", autocomplete: "off"})
38375      */
38376     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38377     /**
38378      * @cfg {String} boxLabel The text that appears beside the checkbox
38379      */
38380     boxLabel : "",
38381     /**
38382      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38383      */  
38384     inputValue : '1',
38385     /**
38386      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38387      */
38388      valueOff: '0', // value when not checked..
38389
38390     actionMode : 'viewEl', 
38391     //
38392     // private
38393     itemCls : 'x-menu-check-item x-form-item',
38394     groupClass : 'x-menu-group-item',
38395     inputType : 'hidden',
38396     
38397     
38398     inSetChecked: false, // check that we are not calling self...
38399     
38400     inputElement: false, // real input element?
38401     basedOn: false, // ????
38402     
38403     isFormField: true, // not sure where this is needed!!!!
38404
38405     onResize : function(){
38406         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38407         if(!this.boxLabel){
38408             this.el.alignTo(this.wrap, 'c-c');
38409         }
38410     },
38411
38412     initEvents : function(){
38413         Roo.form.Checkbox.superclass.initEvents.call(this);
38414         this.el.on("click", this.onClick,  this);
38415         this.el.on("change", this.onClick,  this);
38416     },
38417
38418
38419     getResizeEl : function(){
38420         return this.wrap;
38421     },
38422
38423     getPositionEl : function(){
38424         return this.wrap;
38425     },
38426
38427     // private
38428     onRender : function(ct, position){
38429         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38430         /*
38431         if(this.inputValue !== undefined){
38432             this.el.dom.value = this.inputValue;
38433         }
38434         */
38435         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38436         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38437         var viewEl = this.wrap.createChild({ 
38438             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38439         this.viewEl = viewEl;   
38440         this.wrap.on('click', this.onClick,  this); 
38441         
38442         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38443         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38444         
38445         
38446         
38447         if(this.boxLabel){
38448             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38449         //    viewEl.on('click', this.onClick,  this); 
38450         }
38451         //if(this.checked){
38452             this.setChecked(this.checked);
38453         //}else{
38454             //this.checked = this.el.dom;
38455         //}
38456
38457     },
38458
38459     // private
38460     initValue : Roo.emptyFn,
38461
38462     /**
38463      * Returns the checked state of the checkbox.
38464      * @return {Boolean} True if checked, else false
38465      */
38466     getValue : function(){
38467         if(this.el){
38468             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38469         }
38470         return this.valueOff;
38471         
38472     },
38473
38474         // private
38475     onClick : function(){ 
38476         this.setChecked(!this.checked);
38477
38478         //if(this.el.dom.checked != this.checked){
38479         //    this.setValue(this.el.dom.checked);
38480        // }
38481     },
38482
38483     /**
38484      * Sets the checked state of the checkbox.
38485      * On is always based on a string comparison between inputValue and the param.
38486      * @param {Boolean/String} value - the value to set 
38487      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38488      */
38489     setValue : function(v,suppressEvent){
38490         
38491         
38492         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38493         //if(this.el && this.el.dom){
38494         //    this.el.dom.checked = this.checked;
38495         //    this.el.dom.defaultChecked = this.checked;
38496         //}
38497         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38498         //this.fireEvent("check", this, this.checked);
38499     },
38500     // private..
38501     setChecked : function(state,suppressEvent)
38502     {
38503         if (this.inSetChecked) {
38504             this.checked = state;
38505             return;
38506         }
38507         
38508     
38509         if(this.wrap){
38510             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38511         }
38512         this.checked = state;
38513         if(suppressEvent !== true){
38514             this.fireEvent('check', this, state);
38515         }
38516         this.inSetChecked = true;
38517         this.el.dom.value = state ? this.inputValue : this.valueOff;
38518         this.inSetChecked = false;
38519         
38520     },
38521     // handle setting of hidden value by some other method!!?!?
38522     setFromHidden: function()
38523     {
38524         if(!this.el){
38525             return;
38526         }
38527         //console.log("SET FROM HIDDEN");
38528         //alert('setFrom hidden');
38529         this.setValue(this.el.dom.value);
38530     },
38531     
38532     onDestroy : function()
38533     {
38534         if(this.viewEl){
38535             Roo.get(this.viewEl).remove();
38536         }
38537          
38538         Roo.form.Checkbox.superclass.onDestroy.call(this);
38539     }
38540
38541 });/*
38542  * Based on:
38543  * Ext JS Library 1.1.1
38544  * Copyright(c) 2006-2007, Ext JS, LLC.
38545  *
38546  * Originally Released Under LGPL - original licence link has changed is not relivant.
38547  *
38548  * Fork - LGPL
38549  * <script type="text/javascript">
38550  */
38551  
38552 /**
38553  * @class Roo.form.Radio
38554  * @extends Roo.form.Checkbox
38555  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38556  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38557  * @constructor
38558  * Creates a new Radio
38559  * @param {Object} config Configuration options
38560  */
38561 Roo.form.Radio = function(){
38562     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38563 };
38564 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38565     inputType: 'radio',
38566
38567     /**
38568      * If this radio is part of a group, it will return the selected value
38569      * @return {String}
38570      */
38571     getGroupValue : function(){
38572         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38573     }
38574 });//<script type="text/javascript">
38575
38576 /*
38577  * Ext JS Library 1.1.1
38578  * Copyright(c) 2006-2007, Ext JS, LLC.
38579  * licensing@extjs.com
38580  * 
38581  * http://www.extjs.com/license
38582  */
38583  
38584  /*
38585   * 
38586   * Known bugs:
38587   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38588   * - IE ? - no idea how much works there.
38589   * 
38590   * 
38591   * 
38592   */
38593  
38594
38595 /**
38596  * @class Ext.form.HtmlEditor
38597  * @extends Ext.form.Field
38598  * Provides a lightweight HTML Editor component.
38599  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38600  * 
38601  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38602  * supported by this editor.</b><br/><br/>
38603  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38604  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38605  */
38606 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38607       /**
38608      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38609      */
38610     toolbars : false,
38611     /**
38612      * @cfg {String} createLinkText The default text for the create link prompt
38613      */
38614     createLinkText : 'Please enter the URL for the link:',
38615     /**
38616      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38617      */
38618     defaultLinkValue : 'http:/'+'/',
38619    
38620      /**
38621      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38622      *                        Roo.resizable.
38623      */
38624     resizable : false,
38625      /**
38626      * @cfg {Number} height (in pixels)
38627      */   
38628     height: 300,
38629    /**
38630      * @cfg {Number} width (in pixels)
38631      */   
38632     width: 500,
38633     
38634     /**
38635      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38636      * 
38637      */
38638     stylesheets: false,
38639     
38640     // id of frame..
38641     frameId: false,
38642     
38643     // private properties
38644     validationEvent : false,
38645     deferHeight: true,
38646     initialized : false,
38647     activated : false,
38648     sourceEditMode : false,
38649     onFocus : Roo.emptyFn,
38650     iframePad:3,
38651     hideMode:'offsets',
38652     
38653     defaultAutoCreate : { // modified by initCompnoent..
38654         tag: "textarea",
38655         style:"width:500px;height:300px;",
38656         autocomplete: "off"
38657     },
38658
38659     // private
38660     initComponent : function(){
38661         this.addEvents({
38662             /**
38663              * @event initialize
38664              * Fires when the editor is fully initialized (including the iframe)
38665              * @param {HtmlEditor} this
38666              */
38667             initialize: true,
38668             /**
38669              * @event activate
38670              * Fires when the editor is first receives the focus. Any insertion must wait
38671              * until after this event.
38672              * @param {HtmlEditor} this
38673              */
38674             activate: true,
38675              /**
38676              * @event beforesync
38677              * Fires before the textarea is updated with content from the editor iframe. Return false
38678              * to cancel the sync.
38679              * @param {HtmlEditor} this
38680              * @param {String} html
38681              */
38682             beforesync: true,
38683              /**
38684              * @event beforepush
38685              * Fires before the iframe editor is updated with content from the textarea. Return false
38686              * to cancel the push.
38687              * @param {HtmlEditor} this
38688              * @param {String} html
38689              */
38690             beforepush: true,
38691              /**
38692              * @event sync
38693              * Fires when the textarea is updated with content from the editor iframe.
38694              * @param {HtmlEditor} this
38695              * @param {String} html
38696              */
38697             sync: true,
38698              /**
38699              * @event push
38700              * Fires when the iframe editor is updated with content from the textarea.
38701              * @param {HtmlEditor} this
38702              * @param {String} html
38703              */
38704             push: true,
38705              /**
38706              * @event editmodechange
38707              * Fires when the editor switches edit modes
38708              * @param {HtmlEditor} this
38709              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38710              */
38711             editmodechange: true,
38712             /**
38713              * @event editorevent
38714              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38715              * @param {HtmlEditor} this
38716              */
38717             editorevent: true
38718         });
38719         this.defaultAutoCreate =  {
38720             tag: "textarea",
38721             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38722             autocomplete: "off"
38723         };
38724     },
38725
38726     /**
38727      * Protected method that will not generally be called directly. It
38728      * is called when the editor creates its toolbar. Override this method if you need to
38729      * add custom toolbar buttons.
38730      * @param {HtmlEditor} editor
38731      */
38732     createToolbar : function(editor){
38733         if (!editor.toolbars || !editor.toolbars.length) {
38734             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38735         }
38736         
38737         for (var i =0 ; i < editor.toolbars.length;i++) {
38738             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38739             editor.toolbars[i].init(editor);
38740         }
38741          
38742         
38743     },
38744
38745     /**
38746      * Protected method that will not generally be called directly. It
38747      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38748      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38749      */
38750     getDocMarkup : function(){
38751         // body styles..
38752         var st = '';
38753         if (this.stylesheets === false) {
38754             
38755             Roo.get(document.head).select('style').each(function(node) {
38756                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38757             });
38758             
38759             Roo.get(document.head).select('link').each(function(node) { 
38760                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38761             });
38762             
38763         } else if (!this.stylesheets.length) {
38764                 // simple..
38765                 st = '<style type="text/css">' +
38766                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38767                    '</style>';
38768         } else {
38769             Roo.each(this.stylesheets, function(s) {
38770                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38771             });
38772             
38773         }
38774         
38775         return '<html><head>' + st  +
38776             //<style type="text/css">' +
38777             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38778             //'</style>' +
38779             ' </head><body></body></html>';
38780     },
38781
38782     // private
38783     onRender : function(ct, position)
38784     {
38785         var _t = this;
38786         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38787         this.el.dom.style.border = '0 none';
38788         this.el.dom.setAttribute('tabIndex', -1);
38789         this.el.addClass('x-hidden');
38790         if(Roo.isIE){ // fix IE 1px bogus margin
38791             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38792         }
38793         this.wrap = this.el.wrap({
38794             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38795         });
38796         
38797         if (this.resizable) {
38798             this.resizeEl = new Roo.Resizable(this.wrap, {
38799                 pinned : true,
38800                 wrap: true,
38801                 dynamic : true,
38802                 minHeight : this.height,
38803                 height: this.height,
38804                 handles : this.resizable,
38805                 width: this.width,
38806                 listeners : {
38807                     resize : function(r, w, h) {
38808                         _t.onResize(w,h); // -something
38809                     }
38810                 }
38811             });
38812             
38813         }
38814
38815         this.frameId = Roo.id();
38816         
38817         this.createToolbar(this);
38818         
38819       
38820         
38821         var iframe = this.wrap.createChild({
38822             tag: 'iframe',
38823             id: this.frameId,
38824             name: this.frameId,
38825             frameBorder : 'no',
38826             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38827         }, this.el
38828         );
38829         
38830        // console.log(iframe);
38831         //this.wrap.dom.appendChild(iframe);
38832
38833         this.iframe = iframe.dom;
38834
38835          this.assignDocWin();
38836         
38837         this.doc.designMode = 'on';
38838        
38839         this.doc.open();
38840         this.doc.write(this.getDocMarkup());
38841         this.doc.close();
38842
38843         
38844         var task = { // must defer to wait for browser to be ready
38845             run : function(){
38846                 //console.log("run task?" + this.doc.readyState);
38847                 this.assignDocWin();
38848                 if(this.doc.body || this.doc.readyState == 'complete'){
38849                     try {
38850                         this.doc.designMode="on";
38851                     } catch (e) {
38852                         return;
38853                     }
38854                     Roo.TaskMgr.stop(task);
38855                     this.initEditor.defer(10, this);
38856                 }
38857             },
38858             interval : 10,
38859             duration:10000,
38860             scope: this
38861         };
38862         Roo.TaskMgr.start(task);
38863
38864         if(!this.width){
38865             this.setSize(this.wrap.getSize());
38866         }
38867         if (this.resizeEl) {
38868             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38869             // should trigger onReize..
38870         }
38871     },
38872
38873     // private
38874     onResize : function(w, h)
38875     {
38876         //Roo.log('resize: ' +w + ',' + h );
38877         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38878         if(this.el && this.iframe){
38879             if(typeof w == 'number'){
38880                 var aw = w - this.wrap.getFrameWidth('lr');
38881                 this.el.setWidth(this.adjustWidth('textarea', aw));
38882                 this.iframe.style.width = aw + 'px';
38883             }
38884             if(typeof h == 'number'){
38885                 var tbh = 0;
38886                 for (var i =0; i < this.toolbars.length;i++) {
38887                     // fixme - ask toolbars for heights?
38888                     tbh += this.toolbars[i].tb.el.getHeight();
38889                     if (this.toolbars[i].footer) {
38890                         tbh += this.toolbars[i].footer.el.getHeight();
38891                     }
38892                 }
38893                 
38894                 
38895                 
38896                 
38897                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38898                 ah -= 5; // knock a few pixes off for look..
38899                 this.el.setHeight(this.adjustWidth('textarea', ah));
38900                 this.iframe.style.height = ah + 'px';
38901                 if(this.doc){
38902                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38903                 }
38904             }
38905         }
38906     },
38907
38908     /**
38909      * Toggles the editor between standard and source edit mode.
38910      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38911      */
38912     toggleSourceEdit : function(sourceEditMode){
38913         
38914         this.sourceEditMode = sourceEditMode === true;
38915         
38916         if(this.sourceEditMode){
38917           
38918             this.syncValue();
38919             this.iframe.className = 'x-hidden';
38920             this.el.removeClass('x-hidden');
38921             this.el.dom.removeAttribute('tabIndex');
38922             this.el.focus();
38923         }else{
38924              
38925             this.pushValue();
38926             this.iframe.className = '';
38927             this.el.addClass('x-hidden');
38928             this.el.dom.setAttribute('tabIndex', -1);
38929             this.deferFocus();
38930         }
38931         this.setSize(this.wrap.getSize());
38932         this.fireEvent('editmodechange', this, this.sourceEditMode);
38933     },
38934
38935     // private used internally
38936     createLink : function(){
38937         var url = prompt(this.createLinkText, this.defaultLinkValue);
38938         if(url && url != 'http:/'+'/'){
38939             this.relayCmd('createlink', url);
38940         }
38941     },
38942
38943     // private (for BoxComponent)
38944     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38945
38946     // private (for BoxComponent)
38947     getResizeEl : function(){
38948         return this.wrap;
38949     },
38950
38951     // private (for BoxComponent)
38952     getPositionEl : function(){
38953         return this.wrap;
38954     },
38955
38956     // private
38957     initEvents : function(){
38958         this.originalValue = this.getValue();
38959     },
38960
38961     /**
38962      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38963      * @method
38964      */
38965     markInvalid : Roo.emptyFn,
38966     /**
38967      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38968      * @method
38969      */
38970     clearInvalid : Roo.emptyFn,
38971
38972     setValue : function(v){
38973         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38974         this.pushValue();
38975     },
38976
38977     /**
38978      * Protected method that will not generally be called directly. If you need/want
38979      * custom HTML cleanup, this is the method you should override.
38980      * @param {String} html The HTML to be cleaned
38981      * return {String} The cleaned HTML
38982      */
38983     cleanHtml : function(html){
38984         html = String(html);
38985         if(html.length > 5){
38986             if(Roo.isSafari){ // strip safari nonsense
38987                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38988             }
38989         }
38990         if(html == '&nbsp;'){
38991             html = '';
38992         }
38993         return html;
38994     },
38995
38996     /**
38997      * Protected method that will not generally be called directly. Syncs the contents
38998      * of the editor iframe with the textarea.
38999      */
39000     syncValue : function(){
39001         if(this.initialized){
39002             var bd = (this.doc.body || this.doc.documentElement);
39003             this.cleanUpPaste();
39004             var html = bd.innerHTML;
39005             if(Roo.isSafari){
39006                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39007                 var m = bs.match(/text-align:(.*?);/i);
39008                 if(m && m[1]){
39009                     html = '<div style="'+m[0]+'">' + html + '</div>';
39010                 }
39011             }
39012             html = this.cleanHtml(html);
39013             if(this.fireEvent('beforesync', this, html) !== false){
39014                 this.el.dom.value = html;
39015                 this.fireEvent('sync', this, html);
39016             }
39017         }
39018     },
39019
39020     /**
39021      * Protected method that will not generally be called directly. Pushes the value of the textarea
39022      * into the iframe editor.
39023      */
39024     pushValue : function(){
39025         if(this.initialized){
39026             var v = this.el.dom.value;
39027             if(v.length < 1){
39028                 v = '&#160;';
39029             }
39030             
39031             if(this.fireEvent('beforepush', this, v) !== false){
39032                 var d = (this.doc.body || this.doc.documentElement);
39033                 d.innerHTML = v;
39034                 this.cleanUpPaste();
39035                 this.el.dom.value = d.innerHTML;
39036                 this.fireEvent('push', this, v);
39037             }
39038         }
39039     },
39040
39041     // private
39042     deferFocus : function(){
39043         this.focus.defer(10, this);
39044     },
39045
39046     // doc'ed in Field
39047     focus : function(){
39048         if(this.win && !this.sourceEditMode){
39049             this.win.focus();
39050         }else{
39051             this.el.focus();
39052         }
39053     },
39054     
39055     assignDocWin: function()
39056     {
39057         var iframe = this.iframe;
39058         
39059          if(Roo.isIE){
39060             this.doc = iframe.contentWindow.document;
39061             this.win = iframe.contentWindow;
39062         } else {
39063             if (!Roo.get(this.frameId)) {
39064                 return;
39065             }
39066             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39067             this.win = Roo.get(this.frameId).dom.contentWindow;
39068         }
39069     },
39070     
39071     // private
39072     initEditor : function(){
39073         //console.log("INIT EDITOR");
39074         this.assignDocWin();
39075         
39076         
39077         
39078         this.doc.designMode="on";
39079         this.doc.open();
39080         this.doc.write(this.getDocMarkup());
39081         this.doc.close();
39082         
39083         var dbody = (this.doc.body || this.doc.documentElement);
39084         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39085         // this copies styles from the containing element into thsi one..
39086         // not sure why we need all of this..
39087         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39088         ss['background-attachment'] = 'fixed'; // w3c
39089         dbody.bgProperties = 'fixed'; // ie
39090         Roo.DomHelper.applyStyles(dbody, ss);
39091         Roo.EventManager.on(this.doc, {
39092             //'mousedown': this.onEditorEvent,
39093             'mouseup': this.onEditorEvent,
39094             'dblclick': this.onEditorEvent,
39095             'click': this.onEditorEvent,
39096             'keyup': this.onEditorEvent,
39097             buffer:100,
39098             scope: this
39099         });
39100         if(Roo.isGecko){
39101             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39102         }
39103         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39104             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39105         }
39106         this.initialized = true;
39107
39108         this.fireEvent('initialize', this);
39109         this.pushValue();
39110     },
39111
39112     // private
39113     onDestroy : function(){
39114         
39115         
39116         
39117         if(this.rendered){
39118             
39119             for (var i =0; i < this.toolbars.length;i++) {
39120                 // fixme - ask toolbars for heights?
39121                 this.toolbars[i].onDestroy();
39122             }
39123             
39124             this.wrap.dom.innerHTML = '';
39125             this.wrap.remove();
39126         }
39127     },
39128
39129     // private
39130     onFirstFocus : function(){
39131         
39132         this.assignDocWin();
39133         
39134         
39135         this.activated = true;
39136         for (var i =0; i < this.toolbars.length;i++) {
39137             this.toolbars[i].onFirstFocus();
39138         }
39139        
39140         if(Roo.isGecko){ // prevent silly gecko errors
39141             this.win.focus();
39142             var s = this.win.getSelection();
39143             if(!s.focusNode || s.focusNode.nodeType != 3){
39144                 var r = s.getRangeAt(0);
39145                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39146                 r.collapse(true);
39147                 this.deferFocus();
39148             }
39149             try{
39150                 this.execCmd('useCSS', true);
39151                 this.execCmd('styleWithCSS', false);
39152             }catch(e){}
39153         }
39154         this.fireEvent('activate', this);
39155     },
39156
39157     // private
39158     adjustFont: function(btn){
39159         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39160         //if(Roo.isSafari){ // safari
39161         //    adjust *= 2;
39162        // }
39163         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39164         if(Roo.isSafari){ // safari
39165             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39166             v =  (v < 10) ? 10 : v;
39167             v =  (v > 48) ? 48 : v;
39168             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39169             
39170         }
39171         
39172         
39173         v = Math.max(1, v+adjust);
39174         
39175         this.execCmd('FontSize', v  );
39176     },
39177
39178     onEditorEvent : function(e){
39179         this.fireEvent('editorevent', this, e);
39180       //  this.updateToolbar();
39181         this.syncValue();
39182     },
39183
39184     insertTag : function(tg)
39185     {
39186         // could be a bit smarter... -> wrap the current selected tRoo..
39187         
39188         this.execCmd("formatblock",   tg);
39189         
39190     },
39191     
39192     insertText : function(txt)
39193     {
39194         
39195         
39196         range = this.createRange();
39197         range.deleteContents();
39198                //alert(Sender.getAttribute('label'));
39199                
39200         range.insertNode(this.doc.createTextNode(txt));
39201     } ,
39202     
39203     // private
39204     relayBtnCmd : function(btn){
39205         this.relayCmd(btn.cmd);
39206     },
39207
39208     /**
39209      * Executes a Midas editor command on the editor document and performs necessary focus and
39210      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39211      * @param {String} cmd The Midas command
39212      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39213      */
39214     relayCmd : function(cmd, value){
39215         this.win.focus();
39216         this.execCmd(cmd, value);
39217         this.fireEvent('editorevent', this);
39218         //this.updateToolbar();
39219         this.deferFocus();
39220     },
39221
39222     /**
39223      * Executes a Midas editor command directly on the editor document.
39224      * For visual commands, you should use {@link #relayCmd} instead.
39225      * <b>This should only be called after the editor is initialized.</b>
39226      * @param {String} cmd The Midas command
39227      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39228      */
39229     execCmd : function(cmd, value){
39230         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39231         this.syncValue();
39232     },
39233
39234    
39235     /**
39236      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39237      * to insert tRoo.
39238      * @param {String} text
39239      */
39240     insertAtCursor : function(text){
39241         if(!this.activated){
39242             return;
39243         }
39244         if(Roo.isIE){
39245             this.win.focus();
39246             var r = this.doc.selection.createRange();
39247             if(r){
39248                 r.collapse(true);
39249                 r.pasteHTML(text);
39250                 this.syncValue();
39251                 this.deferFocus();
39252             }
39253         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39254             this.win.focus();
39255             this.execCmd('InsertHTML', text);
39256             this.deferFocus();
39257         }
39258     },
39259  // private
39260     mozKeyPress : function(e){
39261         if(e.ctrlKey){
39262             var c = e.getCharCode(), cmd;
39263           
39264             if(c > 0){
39265                 c = String.fromCharCode(c).toLowerCase();
39266                 switch(c){
39267                     case 'b':
39268                         cmd = 'bold';
39269                     break;
39270                     case 'i':
39271                         cmd = 'italic';
39272                     break;
39273                     case 'u':
39274                         cmd = 'underline';
39275                     case 'v':
39276                         this.cleanUpPaste.defer(100, this);
39277                         return;
39278                     break;
39279                 }
39280                 if(cmd){
39281                     this.win.focus();
39282                     this.execCmd(cmd);
39283                     this.deferFocus();
39284                     e.preventDefault();
39285                 }
39286                 
39287             }
39288         }
39289     },
39290
39291     // private
39292     fixKeys : function(){ // load time branching for fastest keydown performance
39293         if(Roo.isIE){
39294             return function(e){
39295                 var k = e.getKey(), r;
39296                 if(k == e.TAB){
39297                     e.stopEvent();
39298                     r = this.doc.selection.createRange();
39299                     if(r){
39300                         r.collapse(true);
39301                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39302                         this.deferFocus();
39303                     }
39304                     return;
39305                 }
39306                 
39307                 if(k == e.ENTER){
39308                     r = this.doc.selection.createRange();
39309                     if(r){
39310                         var target = r.parentElement();
39311                         if(!target || target.tagName.toLowerCase() != 'li'){
39312                             e.stopEvent();
39313                             r.pasteHTML('<br />');
39314                             r.collapse(false);
39315                             r.select();
39316                         }
39317                     }
39318                 }
39319                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39320                     this.cleanUpPaste.defer(100, this);
39321                     return;
39322                 }
39323                 
39324                 
39325             };
39326         }else if(Roo.isOpera){
39327             return function(e){
39328                 var k = e.getKey();
39329                 if(k == e.TAB){
39330                     e.stopEvent();
39331                     this.win.focus();
39332                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39333                     this.deferFocus();
39334                 }
39335                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39336                     this.cleanUpPaste.defer(100, this);
39337                     return;
39338                 }
39339                 
39340             };
39341         }else if(Roo.isSafari){
39342             return function(e){
39343                 var k = e.getKey();
39344                 
39345                 if(k == e.TAB){
39346                     e.stopEvent();
39347                     this.execCmd('InsertText','\t');
39348                     this.deferFocus();
39349                     return;
39350                 }
39351                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39352                     this.cleanUpPaste.defer(100, this);
39353                     return;
39354                 }
39355                 
39356              };
39357         }
39358     }(),
39359     
39360     getAllAncestors: function()
39361     {
39362         var p = this.getSelectedNode();
39363         var a = [];
39364         if (!p) {
39365             a.push(p); // push blank onto stack..
39366             p = this.getParentElement();
39367         }
39368         
39369         
39370         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39371             a.push(p);
39372             p = p.parentNode;
39373         }
39374         a.push(this.doc.body);
39375         return a;
39376     },
39377     lastSel : false,
39378     lastSelNode : false,
39379     
39380     
39381     getSelection : function() 
39382     {
39383         this.assignDocWin();
39384         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39385     },
39386     
39387     getSelectedNode: function() 
39388     {
39389         // this may only work on Gecko!!!
39390         
39391         // should we cache this!!!!
39392         
39393         
39394         
39395          
39396         var range = this.createRange(this.getSelection()).cloneRange();
39397         
39398         if (Roo.isIE) {
39399             var parent = range.parentElement();
39400             while (true) {
39401                 var testRange = range.duplicate();
39402                 testRange.moveToElementText(parent);
39403                 if (testRange.inRange(range)) {
39404                     break;
39405                 }
39406                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39407                     break;
39408                 }
39409                 parent = parent.parentElement;
39410             }
39411             return parent;
39412         }
39413         
39414         // is ancestor a text element.
39415         var ac =  range.commonAncestorContainer;
39416         if (ac.nodeType == 3) {
39417             ac = ac.parentNode;
39418         }
39419         
39420         var ar = ac.childNodes;
39421          
39422         var nodes = [];
39423         var other_nodes = [];
39424         var has_other_nodes = false;
39425         for (var i=0;i<ar.length;i++) {
39426             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39427                 continue;
39428             }
39429             // fullly contained node.
39430             
39431             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39432                 nodes.push(ar[i]);
39433                 continue;
39434             }
39435             
39436             // probably selected..
39437             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39438                 other_nodes.push(ar[i]);
39439                 continue;
39440             }
39441             // outer..
39442             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39443                 continue;
39444             }
39445             
39446             
39447             has_other_nodes = true;
39448         }
39449         if (!nodes.length && other_nodes.length) {
39450             nodes= other_nodes;
39451         }
39452         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39453             return false;
39454         }
39455         
39456         return nodes[0];
39457     },
39458     createRange: function(sel)
39459     {
39460         // this has strange effects when using with 
39461         // top toolbar - not sure if it's a great idea.
39462         //this.editor.contentWindow.focus();
39463         if (typeof sel != "undefined") {
39464             try {
39465                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39466             } catch(e) {
39467                 return this.doc.createRange();
39468             }
39469         } else {
39470             return this.doc.createRange();
39471         }
39472     },
39473     getParentElement: function()
39474     {
39475         
39476         this.assignDocWin();
39477         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39478         
39479         var range = this.createRange(sel);
39480          
39481         try {
39482             var p = range.commonAncestorContainer;
39483             while (p.nodeType == 3) { // text node
39484                 p = p.parentNode;
39485             }
39486             return p;
39487         } catch (e) {
39488             return null;
39489         }
39490     
39491     },
39492     /***
39493      *
39494      * Range intersection.. the hard stuff...
39495      *  '-1' = before
39496      *  '0' = hits..
39497      *  '1' = after.
39498      *         [ -- selected range --- ]
39499      *   [fail]                        [fail]
39500      *
39501      *    basically..
39502      *      if end is before start or  hits it. fail.
39503      *      if start is after end or hits it fail.
39504      *
39505      *   if either hits (but other is outside. - then it's not 
39506      *   
39507      *    
39508      **/
39509     
39510     
39511     // @see http://www.thismuchiknow.co.uk/?p=64.
39512     rangeIntersectsNode : function(range, node)
39513     {
39514         var nodeRange = node.ownerDocument.createRange();
39515         try {
39516             nodeRange.selectNode(node);
39517         } catch (e) {
39518             nodeRange.selectNodeContents(node);
39519         }
39520     
39521         var rangeStartRange = range.cloneRange();
39522         rangeStartRange.collapse(true);
39523     
39524         var rangeEndRange = range.cloneRange();
39525         rangeEndRange.collapse(false);
39526     
39527         var nodeStartRange = nodeRange.cloneRange();
39528         nodeStartRange.collapse(true);
39529     
39530         var nodeEndRange = nodeRange.cloneRange();
39531         nodeEndRange.collapse(false);
39532     
39533         return rangeStartRange.compareBoundaryPoints(
39534                  Range.START_TO_START, nodeEndRange) == -1 &&
39535                rangeEndRange.compareBoundaryPoints(
39536                  Range.START_TO_START, nodeStartRange) == 1;
39537         
39538          
39539     },
39540     rangeCompareNode : function(range, node)
39541     {
39542         var nodeRange = node.ownerDocument.createRange();
39543         try {
39544             nodeRange.selectNode(node);
39545         } catch (e) {
39546             nodeRange.selectNodeContents(node);
39547         }
39548         
39549         
39550         range.collapse(true);
39551     
39552         nodeRange.collapse(true);
39553      
39554         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39555         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39556          
39557         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39558         
39559         var nodeIsBefore   =  ss == 1;
39560         var nodeIsAfter    = ee == -1;
39561         
39562         if (nodeIsBefore && nodeIsAfter)
39563             return 0; // outer
39564         if (!nodeIsBefore && nodeIsAfter)
39565             return 1; //right trailed.
39566         
39567         if (nodeIsBefore && !nodeIsAfter)
39568             return 2;  // left trailed.
39569         // fully contined.
39570         return 3;
39571     },
39572
39573     // private? - in a new class?
39574     cleanUpPaste :  function()
39575     {
39576         // cleans up the whole document..
39577       //  console.log('cleanuppaste');
39578         this.cleanUpChildren(this.doc.body);
39579         
39580         
39581     },
39582     cleanUpChildren : function (n)
39583     {
39584         if (!n.childNodes.length) {
39585             return;
39586         }
39587         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39588            this.cleanUpChild(n.childNodes[i]);
39589         }
39590     },
39591     
39592     
39593         
39594     
39595     cleanUpChild : function (node)
39596     {
39597         //console.log(node);
39598         if (node.nodeName == "#text") {
39599             // clean up silly Windows -- stuff?
39600             return; 
39601         }
39602         if (node.nodeName == "#comment") {
39603             node.parentNode.removeChild(node);
39604             // clean up silly Windows -- stuff?
39605             return; 
39606         }
39607         
39608         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39609             // remove node.
39610             node.parentNode.removeChild(node);
39611             return;
39612             
39613         }
39614         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39615             this.cleanUpChildren(node);
39616             // inserts everything just before this node...
39617             while (node.childNodes.length) {
39618                 var cn = node.childNodes[0];
39619                 node.removeChild(cn);
39620                 node.parentNode.insertBefore(cn, node);
39621             }
39622             node.parentNode.removeChild(node);
39623             return;
39624         }
39625         
39626         if (!node.attributes || !node.attributes.length) {
39627             this.cleanUpChildren(node);
39628             return;
39629         }
39630         
39631         function cleanAttr(n,v)
39632         {
39633             
39634             if (v.match(/^\./) || v.match(/^\//)) {
39635                 return;
39636             }
39637             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39638                 return;
39639             }
39640             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39641             node.removeAttribute(n);
39642             
39643         }
39644         
39645         function cleanStyle(n,v)
39646         {
39647             if (v.match(/expression/)) { //XSS?? should we even bother..
39648                 node.removeAttribute(n);
39649                 return;
39650             }
39651             
39652             
39653             var parts = v.split(/;/);
39654             Roo.each(parts, function(p) {
39655                 p = p.replace(/\s+/g,'');
39656                 if (!p.length) {
39657                     return true;
39658                 }
39659                 var l = p.split(':').shift().replace(/\s+/g,'');
39660                 
39661                 // only allow 'c whitelisted system attributes'
39662                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39663                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39664                     node.removeAttribute(n);
39665                     return false;
39666                 }
39667                 return true;
39668             });
39669             
39670             
39671         }
39672         
39673         
39674         for (var i = node.attributes.length-1; i > -1 ; i--) {
39675             var a = node.attributes[i];
39676             //console.log(a);
39677             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39678                 node.removeAttribute(a.name);
39679                 return;
39680             }
39681             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39682                 cleanAttr(a.name,a.value); // fixme..
39683                 return;
39684             }
39685             if (a.name == 'style') {
39686                 cleanStyle(a.name,a.value);
39687             }
39688             /// clean up MS crap..
39689             if (a.name == 'class') {
39690                 if (a.value.match(/^Mso/)) {
39691                     node.className = '';
39692                 }
39693             }
39694             
39695             // style cleanup!?
39696             // class cleanup?
39697             
39698         }
39699         
39700         
39701         this.cleanUpChildren(node);
39702         
39703         
39704     }
39705     
39706     
39707     // hide stuff that is not compatible
39708     /**
39709      * @event blur
39710      * @hide
39711      */
39712     /**
39713      * @event change
39714      * @hide
39715      */
39716     /**
39717      * @event focus
39718      * @hide
39719      */
39720     /**
39721      * @event specialkey
39722      * @hide
39723      */
39724     /**
39725      * @cfg {String} fieldClass @hide
39726      */
39727     /**
39728      * @cfg {String} focusClass @hide
39729      */
39730     /**
39731      * @cfg {String} autoCreate @hide
39732      */
39733     /**
39734      * @cfg {String} inputType @hide
39735      */
39736     /**
39737      * @cfg {String} invalidClass @hide
39738      */
39739     /**
39740      * @cfg {String} invalidText @hide
39741      */
39742     /**
39743      * @cfg {String} msgFx @hide
39744      */
39745     /**
39746      * @cfg {String} validateOnBlur @hide
39747      */
39748 });
39749
39750 Roo.form.HtmlEditor.white = [
39751         'area', 'br', 'img', 'input', 'hr', 'wbr',
39752         
39753        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39754        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39755        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39756        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39757        'table',   'ul',         'xmp', 
39758        
39759        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39760       'thead',   'tr', 
39761      
39762       'dir', 'menu', 'ol', 'ul', 'dl',
39763        
39764       'embed',  'object'
39765 ];
39766
39767
39768 Roo.form.HtmlEditor.black = [
39769     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39770         'applet', // 
39771         'base',   'basefont', 'bgsound', 'blink',  'body', 
39772         'frame',  'frameset', 'head',    'html',   'ilayer', 
39773         'iframe', 'layer',  'link',     'meta',    'object',   
39774         'script', 'style' ,'title',  'xml' // clean later..
39775 ];
39776 Roo.form.HtmlEditor.clean = [
39777     'script', 'style', 'title', 'xml'
39778 ];
39779 Roo.form.HtmlEditor.remove = [
39780     'font'
39781 ];
39782 // attributes..
39783
39784 Roo.form.HtmlEditor.ablack = [
39785     'on'
39786 ];
39787     
39788 Roo.form.HtmlEditor.aclean = [ 
39789     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39790 ];
39791
39792 // protocols..
39793 Roo.form.HtmlEditor.pwhite= [
39794         'http',  'https',  'mailto'
39795 ];
39796
39797 // white listed style attributes.
39798 Roo.form.HtmlEditor.cwhite= [
39799         'text-align',
39800         'font-size'
39801 ];
39802
39803 // <script type="text/javascript">
39804 /*
39805  * Based on
39806  * Ext JS Library 1.1.1
39807  * Copyright(c) 2006-2007, Ext JS, LLC.
39808  *  
39809  
39810  */
39811
39812 /**
39813  * @class Roo.form.HtmlEditorToolbar1
39814  * Basic Toolbar
39815  * 
39816  * Usage:
39817  *
39818  new Roo.form.HtmlEditor({
39819     ....
39820     toolbars : [
39821         new Roo.form.HtmlEditorToolbar1({
39822             disable : { fonts: 1 , format: 1, ..., ... , ...],
39823             btns : [ .... ]
39824         })
39825     }
39826      
39827  * 
39828  * @cfg {Object} disable List of elements to disable..
39829  * @cfg {Array} btns List of additional buttons.
39830  * 
39831  * 
39832  * NEEDS Extra CSS? 
39833  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39834  */
39835  
39836 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39837 {
39838     
39839     Roo.apply(this, config);
39840     
39841     // default disabled, based on 'good practice'..
39842     this.disable = this.disable || {};
39843     Roo.applyIf(this.disable, {
39844         fontSize : true,
39845         colors : true,
39846         specialElements : true
39847     });
39848     
39849     
39850     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39851     // dont call parent... till later.
39852 }
39853
39854 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39855     
39856     tb: false,
39857     
39858     rendered: false,
39859     
39860     editor : false,
39861     /**
39862      * @cfg {Object} disable  List of toolbar elements to disable
39863          
39864      */
39865     disable : false,
39866       /**
39867      * @cfg {Array} fontFamilies An array of available font families
39868      */
39869     fontFamilies : [
39870         'Arial',
39871         'Courier New',
39872         'Tahoma',
39873         'Times New Roman',
39874         'Verdana'
39875     ],
39876     
39877     specialChars : [
39878            "&#169;",
39879           "&#174;",     
39880           "&#8482;",    
39881           "&#163;" ,    
39882          // "&#8212;",    
39883           "&#8230;",    
39884           "&#247;" ,    
39885         //  "&#225;" ,     ?? a acute?
39886            "&#8364;"    , //Euro
39887        //   "&#8220;"    ,
39888         //  "&#8221;"    ,
39889         //  "&#8226;"    ,
39890           "&#176;"  //   , // degrees
39891
39892          // "&#233;"     , // e ecute
39893          // "&#250;"     , // u ecute?
39894     ],
39895     
39896     specialElements : [
39897         {
39898             text: "Insert Table",
39899             xtype: 'MenuItem',
39900             xns : Roo.Menu,
39901             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39902                 
39903         },
39904         {    
39905             text: "Insert Image",
39906             xtype: 'MenuItem',
39907             xns : Roo.Menu,
39908             ihtml : '<img src="about:blank"/>'
39909             
39910         }
39911         
39912          
39913     ],
39914     
39915     
39916     inputElements : [ 
39917             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39918             "input:submit", "input:button", "select", "textarea", "label" ],
39919     formats : [
39920         ["p"] ,  
39921         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39922         ["pre"],[ "code"], 
39923         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39924     ],
39925      /**
39926      * @cfg {String} defaultFont default font to use.
39927      */
39928     defaultFont: 'tahoma',
39929    
39930     fontSelect : false,
39931     
39932     
39933     formatCombo : false,
39934     
39935     init : function(editor)
39936     {
39937         this.editor = editor;
39938         
39939         
39940         var fid = editor.frameId;
39941         var etb = this;
39942         function btn(id, toggle, handler){
39943             var xid = fid + '-'+ id ;
39944             return {
39945                 id : xid,
39946                 cmd : id,
39947                 cls : 'x-btn-icon x-edit-'+id,
39948                 enableToggle:toggle !== false,
39949                 scope: editor, // was editor...
39950                 handler:handler||editor.relayBtnCmd,
39951                 clickEvent:'mousedown',
39952                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39953                 tabIndex:-1
39954             };
39955         }
39956         
39957         
39958         
39959         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39960         this.tb = tb;
39961          // stop form submits
39962         tb.el.on('click', function(e){
39963             e.preventDefault(); // what does this do?
39964         });
39965
39966         if(!this.disable.font && !Roo.isSafari){
39967             /* why no safari for fonts
39968             editor.fontSelect = tb.el.createChild({
39969                 tag:'select',
39970                 tabIndex: -1,
39971                 cls:'x-font-select',
39972                 html: editor.createFontOptions()
39973             });
39974             editor.fontSelect.on('change', function(){
39975                 var font = editor.fontSelect.dom.value;
39976                 editor.relayCmd('fontname', font);
39977                 editor.deferFocus();
39978             }, editor);
39979             tb.add(
39980                 editor.fontSelect.dom,
39981                 '-'
39982             );
39983             */
39984         };
39985         if(!this.disable.formats){
39986             this.formatCombo = new Roo.form.ComboBox({
39987                 store: new Roo.data.SimpleStore({
39988                     id : 'tag',
39989                     fields: ['tag'],
39990                     data : this.formats // from states.js
39991                 }),
39992                 blockFocus : true,
39993                 //autoCreate : {tag: "div",  size: "20"},
39994                 displayField:'tag',
39995                 typeAhead: false,
39996                 mode: 'local',
39997                 editable : false,
39998                 triggerAction: 'all',
39999                 emptyText:'Add tag',
40000                 selectOnFocus:true,
40001                 width:135,
40002                 listeners : {
40003                     'select': function(c, r, i) {
40004                         editor.insertTag(r.get('tag'));
40005                         editor.focus();
40006                     }
40007                 }
40008
40009             });
40010             tb.addField(this.formatCombo);
40011             
40012         }
40013         
40014         if(!this.disable.format){
40015             tb.add(
40016                 btn('bold'),
40017                 btn('italic'),
40018                 btn('underline')
40019             );
40020         };
40021         if(!this.disable.fontSize){
40022             tb.add(
40023                 '-',
40024                 
40025                 
40026                 btn('increasefontsize', false, editor.adjustFont),
40027                 btn('decreasefontsize', false, editor.adjustFont)
40028             );
40029         };
40030         
40031         
40032         if(!this.disable.colors){
40033             tb.add(
40034                 '-', {
40035                     id:editor.frameId +'-forecolor',
40036                     cls:'x-btn-icon x-edit-forecolor',
40037                     clickEvent:'mousedown',
40038                     tooltip: this.buttonTips['forecolor'] || undefined,
40039                     tabIndex:-1,
40040                     menu : new Roo.menu.ColorMenu({
40041                         allowReselect: true,
40042                         focus: Roo.emptyFn,
40043                         value:'000000',
40044                         plain:true,
40045                         selectHandler: function(cp, color){
40046                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40047                             editor.deferFocus();
40048                         },
40049                         scope: editor,
40050                         clickEvent:'mousedown'
40051                     })
40052                 }, {
40053                     id:editor.frameId +'backcolor',
40054                     cls:'x-btn-icon x-edit-backcolor',
40055                     clickEvent:'mousedown',
40056                     tooltip: this.buttonTips['backcolor'] || undefined,
40057                     tabIndex:-1,
40058                     menu : new Roo.menu.ColorMenu({
40059                         focus: Roo.emptyFn,
40060                         value:'FFFFFF',
40061                         plain:true,
40062                         allowReselect: true,
40063                         selectHandler: function(cp, color){
40064                             if(Roo.isGecko){
40065                                 editor.execCmd('useCSS', false);
40066                                 editor.execCmd('hilitecolor', color);
40067                                 editor.execCmd('useCSS', true);
40068                                 editor.deferFocus();
40069                             }else{
40070                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40071                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40072                                 editor.deferFocus();
40073                             }
40074                         },
40075                         scope:editor,
40076                         clickEvent:'mousedown'
40077                     })
40078                 }
40079             );
40080         };
40081         // now add all the items...
40082         
40083
40084         if(!this.disable.alignments){
40085             tb.add(
40086                 '-',
40087                 btn('justifyleft'),
40088                 btn('justifycenter'),
40089                 btn('justifyright')
40090             );
40091         };
40092
40093         //if(!Roo.isSafari){
40094             if(!this.disable.links){
40095                 tb.add(
40096                     '-',
40097                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40098                 );
40099             };
40100
40101             if(!this.disable.lists){
40102                 tb.add(
40103                     '-',
40104                     btn('insertorderedlist'),
40105                     btn('insertunorderedlist')
40106                 );
40107             }
40108             if(!this.disable.sourceEdit){
40109                 tb.add(
40110                     '-',
40111                     btn('sourceedit', true, function(btn){
40112                         this.toggleSourceEdit(btn.pressed);
40113                     })
40114                 );
40115             }
40116         //}
40117         
40118         var smenu = { };
40119         // special menu.. - needs to be tidied up..
40120         if (!this.disable.special) {
40121             smenu = {
40122                 text: "&#169;",
40123                 cls: 'x-edit-none',
40124                 
40125                 menu : {
40126                     items : []
40127                 }
40128             };
40129             for (var i =0; i < this.specialChars.length; i++) {
40130                 smenu.menu.items.push({
40131                     
40132                     html: this.specialChars[i],
40133                     handler: function(a,b) {
40134                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40135                         
40136                     },
40137                     tabIndex:-1
40138                 });
40139             }
40140             
40141             
40142             tb.add(smenu);
40143             
40144             
40145         }
40146          
40147         if (!this.disable.specialElements) {
40148             var semenu = {
40149                 text: "Other;",
40150                 cls: 'x-edit-none',
40151                 menu : {
40152                     items : []
40153                 }
40154             };
40155             for (var i =0; i < this.specialElements.length; i++) {
40156                 semenu.menu.items.push(
40157                     Roo.apply({ 
40158                         handler: function(a,b) {
40159                             editor.insertAtCursor(this.ihtml);
40160                         }
40161                     }, this.specialElements[i])
40162                 );
40163                     
40164             }
40165             
40166             tb.add(semenu);
40167             
40168             
40169         }
40170          
40171         
40172         if (this.btns) {
40173             for(var i =0; i< this.btns.length;i++) {
40174                 var b = this.btns[i];
40175                 b.cls =  'x-edit-none';
40176                 b.scope = editor;
40177                 tb.add(b);
40178             }
40179         
40180         }
40181         
40182         
40183         
40184         // disable everything...
40185         
40186         this.tb.items.each(function(item){
40187            if(item.id != editor.frameId+ '-sourceedit'){
40188                 item.disable();
40189             }
40190         });
40191         this.rendered = true;
40192         
40193         // the all the btns;
40194         editor.on('editorevent', this.updateToolbar, this);
40195         // other toolbars need to implement this..
40196         //editor.on('editmodechange', this.updateToolbar, this);
40197     },
40198     
40199     
40200     
40201     /**
40202      * Protected method that will not generally be called directly. It triggers
40203      * a toolbar update by reading the markup state of the current selection in the editor.
40204      */
40205     updateToolbar: function(){
40206
40207         if(!this.editor.activated){
40208             this.editor.onFirstFocus();
40209             return;
40210         }
40211
40212         var btns = this.tb.items.map, 
40213             doc = this.editor.doc,
40214             frameId = this.editor.frameId;
40215
40216         if(!this.disable.font && !Roo.isSafari){
40217             /*
40218             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40219             if(name != this.fontSelect.dom.value){
40220                 this.fontSelect.dom.value = name;
40221             }
40222             */
40223         }
40224         if(!this.disable.format){
40225             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40226             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40227             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40228         }
40229         if(!this.disable.alignments){
40230             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40231             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40232             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40233         }
40234         if(!Roo.isSafari && !this.disable.lists){
40235             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40236             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40237         }
40238         
40239         var ans = this.editor.getAllAncestors();
40240         if (this.formatCombo) {
40241             
40242             
40243             var store = this.formatCombo.store;
40244             this.formatCombo.setValue("");
40245             for (var i =0; i < ans.length;i++) {
40246                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40247                     // select it..
40248                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40249                     break;
40250                 }
40251             }
40252         }
40253         
40254         
40255         
40256         // hides menus... - so this cant be on a menu...
40257         Roo.menu.MenuMgr.hideAll();
40258
40259         //this.editorsyncValue();
40260     },
40261    
40262     
40263     createFontOptions : function(){
40264         var buf = [], fs = this.fontFamilies, ff, lc;
40265         for(var i = 0, len = fs.length; i< len; i++){
40266             ff = fs[i];
40267             lc = ff.toLowerCase();
40268             buf.push(
40269                 '<option value="',lc,'" style="font-family:',ff,';"',
40270                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40271                     ff,
40272                 '</option>'
40273             );
40274         }
40275         return buf.join('');
40276     },
40277     
40278     toggleSourceEdit : function(sourceEditMode){
40279         if(sourceEditMode === undefined){
40280             sourceEditMode = !this.sourceEditMode;
40281         }
40282         this.sourceEditMode = sourceEditMode === true;
40283         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40284         // just toggle the button?
40285         if(btn.pressed !== this.editor.sourceEditMode){
40286             btn.toggle(this.editor.sourceEditMode);
40287             return;
40288         }
40289         
40290         if(this.sourceEditMode){
40291             this.tb.items.each(function(item){
40292                 if(item.cmd != 'sourceedit'){
40293                     item.disable();
40294                 }
40295             });
40296           
40297         }else{
40298             if(this.initialized){
40299                 this.tb.items.each(function(item){
40300                     item.enable();
40301                 });
40302             }
40303             
40304         }
40305         // tell the editor that it's been pressed..
40306         this.editor.toggleSourceEdit(sourceEditMode);
40307        
40308     },
40309      /**
40310      * Object collection of toolbar tooltips for the buttons in the editor. The key
40311      * is the command id associated with that button and the value is a valid QuickTips object.
40312      * For example:
40313 <pre><code>
40314 {
40315     bold : {
40316         title: 'Bold (Ctrl+B)',
40317         text: 'Make the selected text bold.',
40318         cls: 'x-html-editor-tip'
40319     },
40320     italic : {
40321         title: 'Italic (Ctrl+I)',
40322         text: 'Make the selected text italic.',
40323         cls: 'x-html-editor-tip'
40324     },
40325     ...
40326 </code></pre>
40327     * @type Object
40328      */
40329     buttonTips : {
40330         bold : {
40331             title: 'Bold (Ctrl+B)',
40332             text: 'Make the selected text bold.',
40333             cls: 'x-html-editor-tip'
40334         },
40335         italic : {
40336             title: 'Italic (Ctrl+I)',
40337             text: 'Make the selected text italic.',
40338             cls: 'x-html-editor-tip'
40339         },
40340         underline : {
40341             title: 'Underline (Ctrl+U)',
40342             text: 'Underline the selected text.',
40343             cls: 'x-html-editor-tip'
40344         },
40345         increasefontsize : {
40346             title: 'Grow Text',
40347             text: 'Increase the font size.',
40348             cls: 'x-html-editor-tip'
40349         },
40350         decreasefontsize : {
40351             title: 'Shrink Text',
40352             text: 'Decrease the font size.',
40353             cls: 'x-html-editor-tip'
40354         },
40355         backcolor : {
40356             title: 'Text Highlight Color',
40357             text: 'Change the background color of the selected text.',
40358             cls: 'x-html-editor-tip'
40359         },
40360         forecolor : {
40361             title: 'Font Color',
40362             text: 'Change the color of the selected text.',
40363             cls: 'x-html-editor-tip'
40364         },
40365         justifyleft : {
40366             title: 'Align Text Left',
40367             text: 'Align text to the left.',
40368             cls: 'x-html-editor-tip'
40369         },
40370         justifycenter : {
40371             title: 'Center Text',
40372             text: 'Center text in the editor.',
40373             cls: 'x-html-editor-tip'
40374         },
40375         justifyright : {
40376             title: 'Align Text Right',
40377             text: 'Align text to the right.',
40378             cls: 'x-html-editor-tip'
40379         },
40380         insertunorderedlist : {
40381             title: 'Bullet List',
40382             text: 'Start a bulleted list.',
40383             cls: 'x-html-editor-tip'
40384         },
40385         insertorderedlist : {
40386             title: 'Numbered List',
40387             text: 'Start a numbered list.',
40388             cls: 'x-html-editor-tip'
40389         },
40390         createlink : {
40391             title: 'Hyperlink',
40392             text: 'Make the selected text a hyperlink.',
40393             cls: 'x-html-editor-tip'
40394         },
40395         sourceedit : {
40396             title: 'Source Edit',
40397             text: 'Switch to source editing mode.',
40398             cls: 'x-html-editor-tip'
40399         }
40400     },
40401     // private
40402     onDestroy : function(){
40403         if(this.rendered){
40404             
40405             this.tb.items.each(function(item){
40406                 if(item.menu){
40407                     item.menu.removeAll();
40408                     if(item.menu.el){
40409                         item.menu.el.destroy();
40410                     }
40411                 }
40412                 item.destroy();
40413             });
40414              
40415         }
40416     },
40417     onFirstFocus: function() {
40418         this.tb.items.each(function(item){
40419            item.enable();
40420         });
40421     }
40422 });
40423
40424
40425
40426
40427 // <script type="text/javascript">
40428 /*
40429  * Based on
40430  * Ext JS Library 1.1.1
40431  * Copyright(c) 2006-2007, Ext JS, LLC.
40432  *  
40433  
40434  */
40435
40436  
40437 /**
40438  * @class Roo.form.HtmlEditor.ToolbarContext
40439  * Context Toolbar
40440  * 
40441  * Usage:
40442  *
40443  new Roo.form.HtmlEditor({
40444     ....
40445     toolbars : [
40446         { xtype: 'ToolbarStandard', styles : {} }
40447         { xtype: 'ToolbarContext', disable : {} }
40448     ]
40449 })
40450
40451      
40452  * 
40453  * @config : {Object} disable List of elements to disable.. (not done yet.)
40454  * @config : {Object} styles  Map of styles available.
40455  * 
40456  */
40457
40458 Roo.form.HtmlEditor.ToolbarContext = function(config)
40459 {
40460     
40461     Roo.apply(this, config);
40462     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40463     // dont call parent... till later.
40464     this.styles = this.styles || {};
40465 }
40466 Roo.form.HtmlEditor.ToolbarContext.types = {
40467     'IMG' : {
40468         width : {
40469             title: "Width",
40470             width: 40
40471         },
40472         height:  {
40473             title: "Height",
40474             width: 40
40475         },
40476         align: {
40477             title: "Align",
40478             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40479             width : 80
40480             
40481         },
40482         border: {
40483             title: "Border",
40484             width: 40
40485         },
40486         alt: {
40487             title: "Alt",
40488             width: 120
40489         },
40490         src : {
40491             title: "Src",
40492             width: 220
40493         }
40494         
40495     },
40496     'A' : {
40497         name : {
40498             title: "Name",
40499             width: 50
40500         },
40501         href:  {
40502             title: "Href",
40503             width: 220
40504         } // border?
40505         
40506     },
40507     'TABLE' : {
40508         rows : {
40509             title: "Rows",
40510             width: 20
40511         },
40512         cols : {
40513             title: "Cols",
40514             width: 20
40515         },
40516         width : {
40517             title: "Width",
40518             width: 40
40519         },
40520         height : {
40521             title: "Height",
40522             width: 40
40523         },
40524         border : {
40525             title: "Border",
40526             width: 20
40527         }
40528     },
40529     'TD' : {
40530         width : {
40531             title: "Width",
40532             width: 40
40533         },
40534         height : {
40535             title: "Height",
40536             width: 40
40537         },   
40538         align: {
40539             title: "Align",
40540             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40541             width: 80
40542         },
40543         valign: {
40544             title: "Valign",
40545             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40546             width: 80
40547         },
40548         colspan: {
40549             title: "Colspan",
40550             width: 20
40551             
40552         }
40553     },
40554     'INPUT' : {
40555         name : {
40556             title: "name",
40557             width: 120
40558         },
40559         value : {
40560             title: "Value",
40561             width: 120
40562         },
40563         width : {
40564             title: "Width",
40565             width: 40
40566         }
40567     },
40568     'LABEL' : {
40569         'for' : {
40570             title: "For",
40571             width: 120
40572         }
40573     },
40574     'TEXTAREA' : {
40575           name : {
40576             title: "name",
40577             width: 120
40578         },
40579         rows : {
40580             title: "Rows",
40581             width: 20
40582         },
40583         cols : {
40584             title: "Cols",
40585             width: 20
40586         }
40587     },
40588     'SELECT' : {
40589         name : {
40590             title: "name",
40591             width: 120
40592         },
40593         selectoptions : {
40594             title: "Options",
40595             width: 200
40596         }
40597     },
40598     
40599     // should we really allow this??
40600     // should this just be 
40601     'BODY' : {
40602         title : {
40603             title: "title",
40604             width: 200,
40605             disabled : true
40606         }
40607     },
40608     '*' : {
40609         // empty..
40610     }
40611 };
40612
40613
40614
40615 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40616     
40617     tb: false,
40618     
40619     rendered: false,
40620     
40621     editor : false,
40622     /**
40623      * @cfg {Object} disable  List of toolbar elements to disable
40624          
40625      */
40626     disable : false,
40627     /**
40628      * @cfg {Object} styles List of styles 
40629      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40630      *
40631      * These must be defined in the page, so they get rendered correctly..
40632      * .headline { }
40633      * TD.underline { }
40634      * 
40635      */
40636     styles : false,
40637     
40638     
40639     
40640     toolbars : false,
40641     
40642     init : function(editor)
40643     {
40644         this.editor = editor;
40645         
40646         
40647         var fid = editor.frameId;
40648         var etb = this;
40649         function btn(id, toggle, handler){
40650             var xid = fid + '-'+ id ;
40651             return {
40652                 id : xid,
40653                 cmd : id,
40654                 cls : 'x-btn-icon x-edit-'+id,
40655                 enableToggle:toggle !== false,
40656                 scope: editor, // was editor...
40657                 handler:handler||editor.relayBtnCmd,
40658                 clickEvent:'mousedown',
40659                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40660                 tabIndex:-1
40661             };
40662         }
40663         // create a new element.
40664         var wdiv = editor.wrap.createChild({
40665                 tag: 'div'
40666             }, editor.wrap.dom.firstChild.nextSibling, true);
40667         
40668         // can we do this more than once??
40669         
40670          // stop form submits
40671       
40672  
40673         // disable everything...
40674         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40675         this.toolbars = {};
40676            
40677         for (var i in  ty) {
40678           
40679             this.toolbars[i] = this.buildToolbar(ty[i],i);
40680         }
40681         this.tb = this.toolbars.BODY;
40682         this.tb.el.show();
40683         this.buildFooter();
40684         this.footer.show();
40685          
40686         this.rendered = true;
40687         
40688         // the all the btns;
40689         editor.on('editorevent', this.updateToolbar, this);
40690         // other toolbars need to implement this..
40691         //editor.on('editmodechange', this.updateToolbar, this);
40692     },
40693     
40694     
40695     
40696     /**
40697      * Protected method that will not generally be called directly. It triggers
40698      * a toolbar update by reading the markup state of the current selection in the editor.
40699      */
40700     updateToolbar: function(ignore_a,ignore_b,sel){
40701
40702         
40703         if(!this.editor.activated){
40704              this.editor.onFirstFocus();
40705             return;
40706         }
40707         var updateFooter = sel ? false : true;
40708         
40709         
40710         var ans = this.editor.getAllAncestors();
40711         
40712         // pick
40713         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40714         
40715         if (!sel) { 
40716             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40717             sel = sel ? sel : this.editor.doc.body;
40718             sel = sel.tagName.length ? sel : this.editor.doc.body;
40719             
40720         }
40721         // pick a menu that exists..
40722         var tn = sel.tagName.toUpperCase();
40723         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40724         
40725         tn = sel.tagName.toUpperCase();
40726         
40727         var lastSel = this.tb.selectedNode
40728         
40729         this.tb.selectedNode = sel;
40730         
40731         // if current menu does not match..
40732         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40733                 
40734             this.tb.el.hide();
40735             ///console.log("show: " + tn);
40736             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40737             this.tb.el.show();
40738             // update name
40739             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40740             
40741             
40742             // update attributes
40743             if (this.tb.fields) {
40744                 this.tb.fields.each(function(e) {
40745                    e.setValue(sel.getAttribute(e.name));
40746                 });
40747             }
40748             
40749             // update styles
40750             var st = this.tb.fields.item(0);
40751             st.store.removeAll();
40752             var cn = sel.className.split(/\s+/);
40753             
40754             var avs = [];
40755             if (this.styles['*']) {
40756                 
40757                 Roo.each(this.styles['*'], function(v) {
40758                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40759                 });
40760             }
40761             if (this.styles[tn]) { 
40762                 Roo.each(this.styles[tn], function(v) {
40763                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40764                 });
40765             }
40766             
40767             st.store.loadData(avs);
40768             st.collapse();
40769             st.setValue(cn);
40770             
40771             // flag our selected Node.
40772             this.tb.selectedNode = sel;
40773            
40774            
40775             Roo.menu.MenuMgr.hideAll();
40776
40777         }
40778         
40779         if (!updateFooter) {
40780             return;
40781         }
40782         // update the footer
40783         //
40784         var html = '';
40785         
40786         this.footerEls = ans.reverse();
40787         Roo.each(this.footerEls, function(a,i) {
40788             if (!a) { return; }
40789             html += html.length ? ' &gt; '  :  '';
40790             
40791             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40792             
40793         });
40794        
40795         // 
40796         var sz = this.footDisp.up('td').getSize();
40797         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40798         this.footDisp.dom.style.marginLeft = '5px';
40799         
40800         this.footDisp.dom.style.overflow = 'hidden';
40801         
40802         this.footDisp.dom.innerHTML = html;
40803             
40804         //this.editorsyncValue();
40805     },
40806    
40807        
40808     // private
40809     onDestroy : function(){
40810         if(this.rendered){
40811             
40812             this.tb.items.each(function(item){
40813                 if(item.menu){
40814                     item.menu.removeAll();
40815                     if(item.menu.el){
40816                         item.menu.el.destroy();
40817                     }
40818                 }
40819                 item.destroy();
40820             });
40821              
40822         }
40823     },
40824     onFirstFocus: function() {
40825         // need to do this for all the toolbars..
40826         this.tb.items.each(function(item){
40827            item.enable();
40828         });
40829     },
40830     buildToolbar: function(tlist, nm)
40831     {
40832         var editor = this.editor;
40833          // create a new element.
40834         var wdiv = editor.wrap.createChild({
40835                 tag: 'div'
40836             }, editor.wrap.dom.firstChild.nextSibling, true);
40837         
40838        
40839         var tb = new Roo.Toolbar(wdiv);
40840         // add the name..
40841         
40842         tb.add(nm+ ":&nbsp;");
40843         
40844         // styles...
40845         if (this.styles) {
40846             
40847             // this needs a multi-select checkbox...
40848             tb.addField( new Roo.form.ComboBox({
40849                 store: new Roo.data.SimpleStore({
40850                     id : 'val',
40851                     fields: ['val', 'selected'],
40852                     data : [] 
40853                 }),
40854                 name : 'className',
40855                 displayField:'val',
40856                 typeAhead: false,
40857                 mode: 'local',
40858                 editable : false,
40859                 triggerAction: 'all',
40860                 emptyText:'Select Style',
40861                 selectOnFocus:true,
40862                 width: 130,
40863                 listeners : {
40864                     'select': function(c, r, i) {
40865                         // initial support only for on class per el..
40866                         tb.selectedNode.className =  r ? r.get('val') : '';
40867                     }
40868                 }
40869     
40870             }));
40871         }
40872             
40873         
40874         
40875         for (var i in tlist) {
40876             
40877             var item = tlist[i];
40878             tb.add(item.title + ":&nbsp;");
40879             
40880             
40881             
40882             
40883             if (item.opts) {
40884                 // opts == pulldown..
40885                 tb.addField( new Roo.form.ComboBox({
40886                     store: new Roo.data.SimpleStore({
40887                         id : 'val',
40888                         fields: ['val'],
40889                         data : item.opts  
40890                     }),
40891                     name : i,
40892                     displayField:'val',
40893                     typeAhead: false,
40894                     mode: 'local',
40895                     editable : false,
40896                     triggerAction: 'all',
40897                     emptyText:'Select',
40898                     selectOnFocus:true,
40899                     width: item.width ? item.width  : 130,
40900                     listeners : {
40901                         'select': function(c, r, i) {
40902                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40903                         }
40904                     }
40905
40906                 }));
40907                 continue;
40908                     
40909                  
40910                 
40911                 tb.addField( new Roo.form.TextField({
40912                     name: i,
40913                     width: 100,
40914                     //allowBlank:false,
40915                     value: ''
40916                 }));
40917                 continue;
40918             }
40919             tb.addField( new Roo.form.TextField({
40920                 name: i,
40921                 width: item.width,
40922                 //allowBlank:true,
40923                 value: '',
40924                 listeners: {
40925                     'change' : function(f, nv, ov) {
40926                         tb.selectedNode.setAttribute(f.name, nv);
40927                     }
40928                 }
40929             }));
40930              
40931         }
40932         tb.el.on('click', function(e){
40933             e.preventDefault(); // what does this do?
40934         });
40935         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40936         tb.el.hide();
40937         tb.name = nm;
40938         // dont need to disable them... as they will get hidden
40939         return tb;
40940          
40941         
40942     },
40943     buildFooter : function()
40944     {
40945         
40946         var fel = this.editor.wrap.createChild();
40947         this.footer = new Roo.Toolbar(fel);
40948         // toolbar has scrolly on left / right?
40949         var footDisp= new Roo.Toolbar.Fill();
40950         var _t = this;
40951         this.footer.add(
40952             {
40953                 text : '&lt;',
40954                 xtype: 'Button',
40955                 handler : function() {
40956                     _t.footDisp.scrollTo('left',0,true)
40957                 }
40958             }
40959         );
40960         this.footer.add( footDisp );
40961         this.footer.add( 
40962             {
40963                 text : '&gt;',
40964                 xtype: 'Button',
40965                 handler : function() {
40966                     // no animation..
40967                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40968                 }
40969             }
40970         );
40971         var fel = Roo.get(footDisp.el);
40972         fel.addClass('x-editor-context');
40973         this.footDispWrap = fel; 
40974         this.footDispWrap.overflow  = 'hidden';
40975         
40976         this.footDisp = fel.createChild();
40977         this.footDispWrap.on('click', this.onContextClick, this)
40978         
40979         
40980     },
40981     onContextClick : function (ev,dom)
40982     {
40983         ev.preventDefault();
40984         var  cn = dom.className;
40985         Roo.log(cn);
40986         if (!cn.match(/x-ed-loc-/)) {
40987             return;
40988         }
40989         var n = cn.split('-').pop();
40990         var ans = this.footerEls;
40991         var sel = ans[n];
40992         
40993          // pick
40994         var range = this.editor.createRange();
40995         
40996         range.selectNodeContents(sel);
40997         //range.selectNode(sel);
40998         
40999         
41000         var selection = this.editor.getSelection();
41001         selection.removeAllRanges();
41002         selection.addRange(range);
41003         
41004         
41005         
41006         this.updateToolbar(null, null, sel);
41007         
41008         
41009     }
41010     
41011     
41012     
41013     
41014     
41015 });
41016
41017
41018
41019
41020
41021 /*
41022  * Based on:
41023  * Ext JS Library 1.1.1
41024  * Copyright(c) 2006-2007, Ext JS, LLC.
41025  *
41026  * Originally Released Under LGPL - original licence link has changed is not relivant.
41027  *
41028  * Fork - LGPL
41029  * <script type="text/javascript">
41030  */
41031  
41032 /**
41033  * @class Roo.form.BasicForm
41034  * @extends Roo.util.Observable
41035  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41036  * @constructor
41037  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41038  * @param {Object} config Configuration options
41039  */
41040 Roo.form.BasicForm = function(el, config){
41041     this.allItems = [];
41042     this.childForms = [];
41043     Roo.apply(this, config);
41044     /*
41045      * The Roo.form.Field items in this form.
41046      * @type MixedCollection
41047      */
41048      
41049      
41050     this.items = new Roo.util.MixedCollection(false, function(o){
41051         return o.id || (o.id = Roo.id());
41052     });
41053     this.addEvents({
41054         /**
41055          * @event beforeaction
41056          * Fires before any action is performed. Return false to cancel the action.
41057          * @param {Form} this
41058          * @param {Action} action The action to be performed
41059          */
41060         beforeaction: true,
41061         /**
41062          * @event actionfailed
41063          * Fires when an action fails.
41064          * @param {Form} this
41065          * @param {Action} action The action that failed
41066          */
41067         actionfailed : true,
41068         /**
41069          * @event actioncomplete
41070          * Fires when an action is completed.
41071          * @param {Form} this
41072          * @param {Action} action The action that completed
41073          */
41074         actioncomplete : true
41075     });
41076     if(el){
41077         this.initEl(el);
41078     }
41079     Roo.form.BasicForm.superclass.constructor.call(this);
41080 };
41081
41082 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41083     /**
41084      * @cfg {String} method
41085      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41086      */
41087     /**
41088      * @cfg {DataReader} reader
41089      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41090      * This is optional as there is built-in support for processing JSON.
41091      */
41092     /**
41093      * @cfg {DataReader} errorReader
41094      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41095      * This is completely optional as there is built-in support for processing JSON.
41096      */
41097     /**
41098      * @cfg {String} url
41099      * The URL to use for form actions if one isn't supplied in the action options.
41100      */
41101     /**
41102      * @cfg {Boolean} fileUpload
41103      * Set to true if this form is a file upload.
41104      */
41105      
41106     /**
41107      * @cfg {Object} baseParams
41108      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41109      */
41110      /**
41111      
41112     /**
41113      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41114      */
41115     timeout: 30,
41116
41117     // private
41118     activeAction : null,
41119
41120     /**
41121      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41122      * or setValues() data instead of when the form was first created.
41123      */
41124     trackResetOnLoad : false,
41125     
41126     
41127     /**
41128      * childForms - used for multi-tab forms
41129      * @type {Array}
41130      */
41131     childForms : false,
41132     
41133     /**
41134      * allItems - full list of fields.
41135      * @type {Array}
41136      */
41137     allItems : false,
41138     
41139     /**
41140      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41141      * element by passing it or its id or mask the form itself by passing in true.
41142      * @type Mixed
41143      */
41144     waitMsgTarget : false,
41145
41146     // private
41147     initEl : function(el){
41148         this.el = Roo.get(el);
41149         this.id = this.el.id || Roo.id();
41150         this.el.on('submit', this.onSubmit, this);
41151         this.el.addClass('x-form');
41152     },
41153
41154     // private
41155     onSubmit : function(e){
41156         e.stopEvent();
41157     },
41158
41159     /**
41160      * Returns true if client-side validation on the form is successful.
41161      * @return Boolean
41162      */
41163     isValid : function(){
41164         var valid = true;
41165         this.items.each(function(f){
41166            if(!f.validate()){
41167                valid = false;
41168            }
41169         });
41170         return valid;
41171     },
41172
41173     /**
41174      * Returns true if any fields in this form have changed since their original load.
41175      * @return Boolean
41176      */
41177     isDirty : function(){
41178         var dirty = false;
41179         this.items.each(function(f){
41180            if(f.isDirty()){
41181                dirty = true;
41182                return false;
41183            }
41184         });
41185         return dirty;
41186     },
41187
41188     /**
41189      * Performs a predefined action (submit or load) or custom actions you define on this form.
41190      * @param {String} actionName The name of the action type
41191      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41192      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41193      * accept other config options):
41194      * <pre>
41195 Property          Type             Description
41196 ----------------  ---------------  ----------------------------------------------------------------------------------
41197 url               String           The url for the action (defaults to the form's url)
41198 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41199 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41200 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41201                                    validate the form on the client (defaults to false)
41202      * </pre>
41203      * @return {BasicForm} this
41204      */
41205     doAction : function(action, options){
41206         if(typeof action == 'string'){
41207             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41208         }
41209         if(this.fireEvent('beforeaction', this, action) !== false){
41210             this.beforeAction(action);
41211             action.run.defer(100, action);
41212         }
41213         return this;
41214     },
41215
41216     /**
41217      * Shortcut to do a submit action.
41218      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41219      * @return {BasicForm} this
41220      */
41221     submit : function(options){
41222         this.doAction('submit', options);
41223         return this;
41224     },
41225
41226     /**
41227      * Shortcut to do a load action.
41228      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41229      * @return {BasicForm} this
41230      */
41231     load : function(options){
41232         this.doAction('load', options);
41233         return this;
41234     },
41235
41236     /**
41237      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41238      * @param {Record} record The record to edit
41239      * @return {BasicForm} this
41240      */
41241     updateRecord : function(record){
41242         record.beginEdit();
41243         var fs = record.fields;
41244         fs.each(function(f){
41245             var field = this.findField(f.name);
41246             if(field){
41247                 record.set(f.name, field.getValue());
41248             }
41249         }, this);
41250         record.endEdit();
41251         return this;
41252     },
41253
41254     /**
41255      * Loads an Roo.data.Record into this form.
41256      * @param {Record} record The record to load
41257      * @return {BasicForm} this
41258      */
41259     loadRecord : function(record){
41260         this.setValues(record.data);
41261         return this;
41262     },
41263
41264     // private
41265     beforeAction : function(action){
41266         var o = action.options;
41267         
41268        
41269         if(this.waitMsgTarget === true){
41270             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41271         }else if(this.waitMsgTarget){
41272             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41273             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41274         }else {
41275             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41276         }
41277          
41278     },
41279
41280     // private
41281     afterAction : function(action, success){
41282         this.activeAction = null;
41283         var o = action.options;
41284         
41285         if(this.waitMsgTarget === true){
41286             this.el.unmask();
41287         }else if(this.waitMsgTarget){
41288             this.waitMsgTarget.unmask();
41289         }else{
41290             Roo.MessageBox.updateProgress(1);
41291             Roo.MessageBox.hide();
41292         }
41293          
41294         if(success){
41295             if(o.reset){
41296                 this.reset();
41297             }
41298             Roo.callback(o.success, o.scope, [this, action]);
41299             this.fireEvent('actioncomplete', this, action);
41300             
41301         }else{
41302             Roo.callback(o.failure, o.scope, [this, action]);
41303             // show an error message if no failed handler is set..
41304             if (!this.hasListener('actionfailed')) {
41305                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41306             }
41307             
41308             this.fireEvent('actionfailed', this, action);
41309         }
41310         
41311     },
41312
41313     /**
41314      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41315      * @param {String} id The value to search for
41316      * @return Field
41317      */
41318     findField : function(id){
41319         var field = this.items.get(id);
41320         if(!field){
41321             this.items.each(function(f){
41322                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41323                     field = f;
41324                     return false;
41325                 }
41326             });
41327         }
41328         return field || null;
41329     },
41330
41331     /**
41332      * Add a secondary form to this one, 
41333      * Used to provide tabbed forms. One form is primary, with hidden values 
41334      * which mirror the elements from the other forms.
41335      * 
41336      * @param {Roo.form.Form} form to add.
41337      * 
41338      */
41339     addForm : function(form)
41340     {
41341        
41342         if (this.childForms.indexOf(form) > -1) {
41343             // already added..
41344             return;
41345         }
41346         this.childForms.push(form);
41347         var n = '';
41348         Roo.each(form.allItems, function (fe) {
41349             
41350             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41351             if (this.findField(n)) { // already added..
41352                 return;
41353             }
41354             var add = new Roo.form.Hidden({
41355                 name : n
41356             });
41357             add.render(this.el);
41358             
41359             this.add( add );
41360         }, this);
41361         
41362     },
41363     /**
41364      * Mark fields in this form invalid in bulk.
41365      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41366      * @return {BasicForm} this
41367      */
41368     markInvalid : function(errors){
41369         if(errors instanceof Array){
41370             for(var i = 0, len = errors.length; i < len; i++){
41371                 var fieldError = errors[i];
41372                 var f = this.findField(fieldError.id);
41373                 if(f){
41374                     f.markInvalid(fieldError.msg);
41375                 }
41376             }
41377         }else{
41378             var field, id;
41379             for(id in errors){
41380                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41381                     field.markInvalid(errors[id]);
41382                 }
41383             }
41384         }
41385         Roo.each(this.childForms || [], function (f) {
41386             f.markInvalid(errors);
41387         });
41388         
41389         return this;
41390     },
41391
41392     /**
41393      * Set values for fields in this form in bulk.
41394      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41395      * @return {BasicForm} this
41396      */
41397     setValues : function(values){
41398         if(values instanceof Array){ // array of objects
41399             for(var i = 0, len = values.length; i < len; i++){
41400                 var v = values[i];
41401                 var f = this.findField(v.id);
41402                 if(f){
41403                     f.setValue(v.value);
41404                     if(this.trackResetOnLoad){
41405                         f.originalValue = f.getValue();
41406                     }
41407                 }
41408             }
41409         }else{ // object hash
41410             var field, id;
41411             for(id in values){
41412                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41413                     
41414                     if (field.setFromData && 
41415                         field.valueField && 
41416                         field.displayField &&
41417                         // combos' with local stores can 
41418                         // be queried via setValue()
41419                         // to set their value..
41420                         (field.store && !field.store.isLocal)
41421                         ) {
41422                         // it's a combo
41423                         var sd = { };
41424                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41425                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41426                         field.setFromData(sd);
41427                         
41428                     } else {
41429                         field.setValue(values[id]);
41430                     }
41431                     
41432                     
41433                     if(this.trackResetOnLoad){
41434                         field.originalValue = field.getValue();
41435                     }
41436                 }
41437             }
41438         }
41439          
41440         Roo.each(this.childForms || [], function (f) {
41441             f.setValues(values);
41442         });
41443                 
41444         return this;
41445     },
41446
41447     /**
41448      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41449      * they are returned as an array.
41450      * @param {Boolean} asString
41451      * @return {Object}
41452      */
41453     getValues : function(asString){
41454         if (this.childForms) {
41455             // copy values from the child forms
41456             Roo.each(this.childForms, function (f) {
41457                 this.setValues(f.getValues());
41458             }, this);
41459         }
41460         
41461         
41462         
41463         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41464         if(asString === true){
41465             return fs;
41466         }
41467         return Roo.urlDecode(fs);
41468     },
41469     
41470     /**
41471      * Returns the fields in this form as an object with key/value pairs. 
41472      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41473      * @return {Object}
41474      */
41475     getFieldValues : function()
41476     {
41477         if (this.childForms) {
41478             // copy values from the child forms
41479             Roo.each(this.childForms, function (f) {
41480                 this.setValues(f.getValues());
41481             }, this);
41482         }
41483         
41484         var ret = {};
41485         this.items.each(function(f){
41486             if (!f.getName()) {
41487                 return;
41488             }
41489             var v = f.getValue();
41490             if ((typeof(v) == 'object') && f.getRawValue) {
41491                 v = f.getRawValue() ; // dates..
41492             }
41493             ret[f.getName()] = v;
41494         });
41495         
41496         return ret;
41497     },
41498
41499     /**
41500      * Clears all invalid messages in this form.
41501      * @return {BasicForm} this
41502      */
41503     clearInvalid : function(){
41504         this.items.each(function(f){
41505            f.clearInvalid();
41506         });
41507         
41508         Roo.each(this.childForms || [], function (f) {
41509             f.clearInvalid();
41510         });
41511         
41512         
41513         return this;
41514     },
41515
41516     /**
41517      * Resets this form.
41518      * @return {BasicForm} this
41519      */
41520     reset : function(){
41521         this.items.each(function(f){
41522             f.reset();
41523         });
41524         
41525         Roo.each(this.childForms || [], function (f) {
41526             f.reset();
41527         });
41528        
41529         
41530         return this;
41531     },
41532
41533     /**
41534      * Add Roo.form components to this form.
41535      * @param {Field} field1
41536      * @param {Field} field2 (optional)
41537      * @param {Field} etc (optional)
41538      * @return {BasicForm} this
41539      */
41540     add : function(){
41541         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41542         return this;
41543     },
41544
41545
41546     /**
41547      * Removes a field from the items collection (does NOT remove its markup).
41548      * @param {Field} field
41549      * @return {BasicForm} this
41550      */
41551     remove : function(field){
41552         this.items.remove(field);
41553         return this;
41554     },
41555
41556     /**
41557      * Looks at the fields in this form, checks them for an id attribute,
41558      * and calls applyTo on the existing dom element with that id.
41559      * @return {BasicForm} this
41560      */
41561     render : function(){
41562         this.items.each(function(f){
41563             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41564                 f.applyTo(f.id);
41565             }
41566         });
41567         return this;
41568     },
41569
41570     /**
41571      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41572      * @param {Object} values
41573      * @return {BasicForm} this
41574      */
41575     applyToFields : function(o){
41576         this.items.each(function(f){
41577            Roo.apply(f, o);
41578         });
41579         return this;
41580     },
41581
41582     /**
41583      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41584      * @param {Object} values
41585      * @return {BasicForm} this
41586      */
41587     applyIfToFields : function(o){
41588         this.items.each(function(f){
41589            Roo.applyIf(f, o);
41590         });
41591         return this;
41592     }
41593 });
41594
41595 // back compat
41596 Roo.BasicForm = Roo.form.BasicForm;/*
41597  * Based on:
41598  * Ext JS Library 1.1.1
41599  * Copyright(c) 2006-2007, Ext JS, LLC.
41600  *
41601  * Originally Released Under LGPL - original licence link has changed is not relivant.
41602  *
41603  * Fork - LGPL
41604  * <script type="text/javascript">
41605  */
41606
41607 /**
41608  * @class Roo.form.Form
41609  * @extends Roo.form.BasicForm
41610  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41611  * @constructor
41612  * @param {Object} config Configuration options
41613  */
41614 Roo.form.Form = function(config){
41615     var xitems =  [];
41616     if (config.items) {
41617         xitems = config.items;
41618         delete config.items;
41619     }
41620    
41621     
41622     Roo.form.Form.superclass.constructor.call(this, null, config);
41623     this.url = this.url || this.action;
41624     if(!this.root){
41625         this.root = new Roo.form.Layout(Roo.applyIf({
41626             id: Roo.id()
41627         }, config));
41628     }
41629     this.active = this.root;
41630     /**
41631      * Array of all the buttons that have been added to this form via {@link addButton}
41632      * @type Array
41633      */
41634     this.buttons = [];
41635     this.allItems = [];
41636     this.addEvents({
41637         /**
41638          * @event clientvalidation
41639          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41640          * @param {Form} this
41641          * @param {Boolean} valid true if the form has passed client-side validation
41642          */
41643         clientvalidation: true,
41644         /**
41645          * @event rendered
41646          * Fires when the form is rendered
41647          * @param {Roo.form.Form} form
41648          */
41649         rendered : true
41650     });
41651     
41652     if (this.progressUrl) {
41653             // push a hidden field onto the list of fields..
41654             this.addxtype( {
41655                     xns: Roo.form, 
41656                     xtype : 'Hidden', 
41657                     name : 'UPLOAD_IDENTIFIER' 
41658             });
41659         }
41660         
41661     
41662     Roo.each(xitems, this.addxtype, this);
41663     
41664     
41665     
41666 };
41667
41668 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41669     /**
41670      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41671      */
41672     /**
41673      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41674      */
41675     /**
41676      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41677      */
41678     buttonAlign:'center',
41679
41680     /**
41681      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41682      */
41683     minButtonWidth:75,
41684
41685     /**
41686      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41687      * This property cascades to child containers if not set.
41688      */
41689     labelAlign:'left',
41690
41691     /**
41692      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41693      * fires a looping event with that state. This is required to bind buttons to the valid
41694      * state using the config value formBind:true on the button.
41695      */
41696     monitorValid : false,
41697
41698     /**
41699      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41700      */
41701     monitorPoll : 200,
41702     
41703     /**
41704      * @cfg {String} progressUrl - Url to return progress data 
41705      */
41706     
41707     progressUrl : false,
41708   
41709     /**
41710      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41711      * fields are added and the column is closed. If no fields are passed the column remains open
41712      * until end() is called.
41713      * @param {Object} config The config to pass to the column
41714      * @param {Field} field1 (optional)
41715      * @param {Field} field2 (optional)
41716      * @param {Field} etc (optional)
41717      * @return Column The column container object
41718      */
41719     column : function(c){
41720         var col = new Roo.form.Column(c);
41721         this.start(col);
41722         if(arguments.length > 1){ // duplicate code required because of Opera
41723             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41724             this.end();
41725         }
41726         return col;
41727     },
41728
41729     /**
41730      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41731      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41732      * until end() is called.
41733      * @param {Object} config The config to pass to the fieldset
41734      * @param {Field} field1 (optional)
41735      * @param {Field} field2 (optional)
41736      * @param {Field} etc (optional)
41737      * @return FieldSet The fieldset container object
41738      */
41739     fieldset : function(c){
41740         var fs = new Roo.form.FieldSet(c);
41741         this.start(fs);
41742         if(arguments.length > 1){ // duplicate code required because of Opera
41743             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41744             this.end();
41745         }
41746         return fs;
41747     },
41748
41749     /**
41750      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41751      * fields are added and the container is closed. If no fields are passed the container remains open
41752      * until end() is called.
41753      * @param {Object} config The config to pass to the Layout
41754      * @param {Field} field1 (optional)
41755      * @param {Field} field2 (optional)
41756      * @param {Field} etc (optional)
41757      * @return Layout The container object
41758      */
41759     container : function(c){
41760         var l = new Roo.form.Layout(c);
41761         this.start(l);
41762         if(arguments.length > 1){ // duplicate code required because of Opera
41763             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41764             this.end();
41765         }
41766         return l;
41767     },
41768
41769     /**
41770      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41771      * @param {Object} container A Roo.form.Layout or subclass of Layout
41772      * @return {Form} this
41773      */
41774     start : function(c){
41775         // cascade label info
41776         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41777         this.active.stack.push(c);
41778         c.ownerCt = this.active;
41779         this.active = c;
41780         return this;
41781     },
41782
41783     /**
41784      * Closes the current open container
41785      * @return {Form} this
41786      */
41787     end : function(){
41788         if(this.active == this.root){
41789             return this;
41790         }
41791         this.active = this.active.ownerCt;
41792         return this;
41793     },
41794
41795     /**
41796      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41797      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41798      * as the label of the field.
41799      * @param {Field} field1
41800      * @param {Field} field2 (optional)
41801      * @param {Field} etc. (optional)
41802      * @return {Form} this
41803      */
41804     add : function(){
41805         this.active.stack.push.apply(this.active.stack, arguments);
41806         this.allItems.push.apply(this.allItems,arguments);
41807         var r = [];
41808         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41809             if(a[i].isFormField){
41810                 r.push(a[i]);
41811             }
41812         }
41813         if(r.length > 0){
41814             Roo.form.Form.superclass.add.apply(this, r);
41815         }
41816         return this;
41817     },
41818     
41819
41820     
41821     
41822     
41823      /**
41824      * Find any element that has been added to a form, using it's ID or name
41825      * This can include framesets, columns etc. along with regular fields..
41826      * @param {String} id - id or name to find.
41827      
41828      * @return {Element} e - or false if nothing found.
41829      */
41830     findbyId : function(id)
41831     {
41832         var ret = false;
41833         if (!id) {
41834             return ret;
41835         }
41836         Roo.each(this.allItems, function(f){
41837             if (f.id == id || f.name == id ){
41838                 ret = f;
41839                 return false;
41840             }
41841         });
41842         return ret;
41843     },
41844
41845     
41846     
41847     /**
41848      * Render this form into the passed container. This should only be called once!
41849      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41850      * @return {Form} this
41851      */
41852     render : function(ct)
41853     {
41854         
41855         
41856         
41857         ct = Roo.get(ct);
41858         var o = this.autoCreate || {
41859             tag: 'form',
41860             method : this.method || 'POST',
41861             id : this.id || Roo.id()
41862         };
41863         this.initEl(ct.createChild(o));
41864
41865         this.root.render(this.el);
41866         
41867        
41868              
41869         this.items.each(function(f){
41870             f.render('x-form-el-'+f.id);
41871         });
41872
41873         if(this.buttons.length > 0){
41874             // tables are required to maintain order and for correct IE layout
41875             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41876                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41877                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41878             }}, null, true);
41879             var tr = tb.getElementsByTagName('tr')[0];
41880             for(var i = 0, len = this.buttons.length; i < len; i++) {
41881                 var b = this.buttons[i];
41882                 var td = document.createElement('td');
41883                 td.className = 'x-form-btn-td';
41884                 b.render(tr.appendChild(td));
41885             }
41886         }
41887         if(this.monitorValid){ // initialize after render
41888             this.startMonitoring();
41889         }
41890         this.fireEvent('rendered', this);
41891         return this;
41892     },
41893
41894     /**
41895      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41896      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41897      * object or a valid Roo.DomHelper element config
41898      * @param {Function} handler The function called when the button is clicked
41899      * @param {Object} scope (optional) The scope of the handler function
41900      * @return {Roo.Button}
41901      */
41902     addButton : function(config, handler, scope){
41903         var bc = {
41904             handler: handler,
41905             scope: scope,
41906             minWidth: this.minButtonWidth,
41907             hideParent:true
41908         };
41909         if(typeof config == "string"){
41910             bc.text = config;
41911         }else{
41912             Roo.apply(bc, config);
41913         }
41914         var btn = new Roo.Button(null, bc);
41915         this.buttons.push(btn);
41916         return btn;
41917     },
41918
41919      /**
41920      * Adds a series of form elements (using the xtype property as the factory method.
41921      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41922      * @param {Object} config 
41923      */
41924     
41925     addxtype : function()
41926     {
41927         var ar = Array.prototype.slice.call(arguments, 0);
41928         var ret = false;
41929         for(var i = 0; i < ar.length; i++) {
41930             if (!ar[i]) {
41931                 continue; // skip -- if this happends something invalid got sent, we 
41932                 // should ignore it, as basically that interface element will not show up
41933                 // and that should be pretty obvious!!
41934             }
41935             
41936             if (Roo.form[ar[i].xtype]) {
41937                 ar[i].form = this;
41938                 var fe = Roo.factory(ar[i], Roo.form);
41939                 if (!ret) {
41940                     ret = fe;
41941                 }
41942                 fe.form = this;
41943                 if (fe.store) {
41944                     fe.store.form = this;
41945                 }
41946                 if (fe.isLayout) {  
41947                          
41948                     this.start(fe);
41949                     this.allItems.push(fe);
41950                     if (fe.items && fe.addxtype) {
41951                         fe.addxtype.apply(fe, fe.items);
41952                         delete fe.items;
41953                     }
41954                      this.end();
41955                     continue;
41956                 }
41957                 
41958                 
41959                  
41960                 this.add(fe);
41961               //  console.log('adding ' + ar[i].xtype);
41962             }
41963             if (ar[i].xtype == 'Button') {  
41964                 //console.log('adding button');
41965                 //console.log(ar[i]);
41966                 this.addButton(ar[i]);
41967                 this.allItems.push(fe);
41968                 continue;
41969             }
41970             
41971             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41972                 alert('end is not supported on xtype any more, use items');
41973             //    this.end();
41974             //    //console.log('adding end');
41975             }
41976             
41977         }
41978         return ret;
41979     },
41980     
41981     /**
41982      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41983      * option "monitorValid"
41984      */
41985     startMonitoring : function(){
41986         if(!this.bound){
41987             this.bound = true;
41988             Roo.TaskMgr.start({
41989                 run : this.bindHandler,
41990                 interval : this.monitorPoll || 200,
41991                 scope: this
41992             });
41993         }
41994     },
41995
41996     /**
41997      * Stops monitoring of the valid state of this form
41998      */
41999     stopMonitoring : function(){
42000         this.bound = false;
42001     },
42002
42003     // private
42004     bindHandler : function(){
42005         if(!this.bound){
42006             return false; // stops binding
42007         }
42008         var valid = true;
42009         this.items.each(function(f){
42010             if(!f.isValid(true)){
42011                 valid = false;
42012                 return false;
42013             }
42014         });
42015         for(var i = 0, len = this.buttons.length; i < len; i++){
42016             var btn = this.buttons[i];
42017             if(btn.formBind === true && btn.disabled === valid){
42018                 btn.setDisabled(!valid);
42019             }
42020         }
42021         this.fireEvent('clientvalidation', this, valid);
42022     }
42023     
42024     
42025     
42026     
42027     
42028     
42029     
42030     
42031 });
42032
42033
42034 // back compat
42035 Roo.Form = Roo.form.Form;
42036 /*
42037  * Based on:
42038  * Ext JS Library 1.1.1
42039  * Copyright(c) 2006-2007, Ext JS, LLC.
42040  *
42041  * Originally Released Under LGPL - original licence link has changed is not relivant.
42042  *
42043  * Fork - LGPL
42044  * <script type="text/javascript">
42045  */
42046  
42047  /**
42048  * @class Roo.form.Action
42049  * Internal Class used to handle form actions
42050  * @constructor
42051  * @param {Roo.form.BasicForm} el The form element or its id
42052  * @param {Object} config Configuration options
42053  */
42054  
42055  
42056 // define the action interface
42057 Roo.form.Action = function(form, options){
42058     this.form = form;
42059     this.options = options || {};
42060 };
42061 /**
42062  * Client Validation Failed
42063  * @const 
42064  */
42065 Roo.form.Action.CLIENT_INVALID = 'client';
42066 /**
42067  * Server Validation Failed
42068  * @const 
42069  */
42070  Roo.form.Action.SERVER_INVALID = 'server';
42071  /**
42072  * Connect to Server Failed
42073  * @const 
42074  */
42075 Roo.form.Action.CONNECT_FAILURE = 'connect';
42076 /**
42077  * Reading Data from Server Failed
42078  * @const 
42079  */
42080 Roo.form.Action.LOAD_FAILURE = 'load';
42081
42082 Roo.form.Action.prototype = {
42083     type : 'default',
42084     failureType : undefined,
42085     response : undefined,
42086     result : undefined,
42087
42088     // interface method
42089     run : function(options){
42090
42091     },
42092
42093     // interface method
42094     success : function(response){
42095
42096     },
42097
42098     // interface method
42099     handleResponse : function(response){
42100
42101     },
42102
42103     // default connection failure
42104     failure : function(response){
42105         
42106         this.response = response;
42107         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42108         this.form.afterAction(this, false);
42109     },
42110
42111     processResponse : function(response){
42112         this.response = response;
42113         if(!response.responseText){
42114             return true;
42115         }
42116         this.result = this.handleResponse(response);
42117         return this.result;
42118     },
42119
42120     // utility functions used internally
42121     getUrl : function(appendParams){
42122         var url = this.options.url || this.form.url || this.form.el.dom.action;
42123         if(appendParams){
42124             var p = this.getParams();
42125             if(p){
42126                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42127             }
42128         }
42129         return url;
42130     },
42131
42132     getMethod : function(){
42133         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42134     },
42135
42136     getParams : function(){
42137         var bp = this.form.baseParams;
42138         var p = this.options.params;
42139         if(p){
42140             if(typeof p == "object"){
42141                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42142             }else if(typeof p == 'string' && bp){
42143                 p += '&' + Roo.urlEncode(bp);
42144             }
42145         }else if(bp){
42146             p = Roo.urlEncode(bp);
42147         }
42148         return p;
42149     },
42150
42151     createCallback : function(){
42152         return {
42153             success: this.success,
42154             failure: this.failure,
42155             scope: this,
42156             timeout: (this.form.timeout*1000),
42157             upload: this.form.fileUpload ? this.success : undefined
42158         };
42159     }
42160 };
42161
42162 Roo.form.Action.Submit = function(form, options){
42163     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42164 };
42165
42166 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42167     type : 'submit',
42168
42169     haveProgress : false,
42170     uploadComplete : false,
42171     
42172     // uploadProgress indicator.
42173     uploadProgress : function()
42174     {
42175         if (!this.form.progressUrl) {
42176             return;
42177         }
42178         
42179         if (!this.haveProgress) {
42180             Roo.MessageBox.progress("Uploading", "Uploading");
42181         }
42182         if (this.uploadComplete) {
42183            Roo.MessageBox.hide();
42184            return;
42185         }
42186         
42187         this.haveProgress = true;
42188    
42189         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42190         
42191         var c = new Roo.data.Connection();
42192         c.request({
42193             url : this.form.progressUrl,
42194             params: {
42195                 id : uid
42196             },
42197             method: 'GET',
42198             success : function(req){
42199                //console.log(data);
42200                 var rdata = false;
42201                 var edata;
42202                 try  {
42203                    rdata = Roo.decode(req.responseText)
42204                 } catch (e) {
42205                     Roo.log("Invalid data from server..");
42206                     Roo.log(edata);
42207                     return;
42208                 }
42209                 if (!rdata || !rdata.success) {
42210                     Roo.log(rdata);
42211                     return;
42212                 }
42213                 var data = rdata.data;
42214                 
42215                 if (this.uploadComplete) {
42216                    Roo.MessageBox.hide();
42217                    return;
42218                 }
42219                    
42220                 if (data){
42221                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42222                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42223                     );
42224                 }
42225                 this.uploadProgress.defer(2000,this);
42226             },
42227        
42228             failure: function(data) {
42229                 Roo.log('progress url failed ');
42230                 Roo.log(data);
42231             },
42232             scope : this
42233         });
42234            
42235     },
42236     
42237     
42238     run : function()
42239     {
42240         // run get Values on the form, so it syncs any secondary forms.
42241         this.form.getValues();
42242         
42243         var o = this.options;
42244         var method = this.getMethod();
42245         var isPost = method == 'POST';
42246         if(o.clientValidation === false || this.form.isValid()){
42247             
42248             if (this.form.progressUrl) {
42249                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42250                     (new Date() * 1) + '' + Math.random());
42251                     
42252             } 
42253             
42254             
42255             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42256                 form:this.form.el.dom,
42257                 url:this.getUrl(!isPost),
42258                 method: method,
42259                 params:isPost ? this.getParams() : null,
42260                 isUpload: this.form.fileUpload
42261             }));
42262             
42263             this.uploadProgress();
42264
42265         }else if (o.clientValidation !== false){ // client validation failed
42266             this.failureType = Roo.form.Action.CLIENT_INVALID;
42267             this.form.afterAction(this, false);
42268         }
42269     },
42270
42271     success : function(response)
42272     {
42273         this.uploadComplete= true;
42274         if (this.haveProgress) {
42275             Roo.MessageBox.hide();
42276         }
42277         
42278         
42279         var result = this.processResponse(response);
42280         if(result === true || result.success){
42281             this.form.afterAction(this, true);
42282             return;
42283         }
42284         if(result.errors){
42285             this.form.markInvalid(result.errors);
42286             this.failureType = Roo.form.Action.SERVER_INVALID;
42287         }
42288         this.form.afterAction(this, false);
42289     },
42290     failure : function(response)
42291     {
42292         this.uploadComplete= true;
42293         if (this.haveProgress) {
42294             Roo.MessageBox.hide();
42295         }
42296         
42297         
42298         this.response = response;
42299         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42300         this.form.afterAction(this, false);
42301     },
42302     
42303     handleResponse : function(response){
42304         if(this.form.errorReader){
42305             var rs = this.form.errorReader.read(response);
42306             var errors = [];
42307             if(rs.records){
42308                 for(var i = 0, len = rs.records.length; i < len; i++) {
42309                     var r = rs.records[i];
42310                     errors[i] = r.data;
42311                 }
42312             }
42313             if(errors.length < 1){
42314                 errors = null;
42315             }
42316             return {
42317                 success : rs.success,
42318                 errors : errors
42319             };
42320         }
42321         var ret = false;
42322         try {
42323             ret = Roo.decode(response.responseText);
42324         } catch (e) {
42325             ret = {
42326                 success: false,
42327                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42328                 errors : []
42329             };
42330         }
42331         return ret;
42332         
42333     }
42334 });
42335
42336
42337 Roo.form.Action.Load = function(form, options){
42338     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42339     this.reader = this.form.reader;
42340 };
42341
42342 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42343     type : 'load',
42344
42345     run : function(){
42346         
42347         Roo.Ajax.request(Roo.apply(
42348                 this.createCallback(), {
42349                     method:this.getMethod(),
42350                     url:this.getUrl(false),
42351                     params:this.getParams()
42352         }));
42353     },
42354
42355     success : function(response){
42356         
42357         var result = this.processResponse(response);
42358         if(result === true || !result.success || !result.data){
42359             this.failureType = Roo.form.Action.LOAD_FAILURE;
42360             this.form.afterAction(this, false);
42361             return;
42362         }
42363         this.form.clearInvalid();
42364         this.form.setValues(result.data);
42365         this.form.afterAction(this, true);
42366     },
42367
42368     handleResponse : function(response){
42369         if(this.form.reader){
42370             var rs = this.form.reader.read(response);
42371             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42372             return {
42373                 success : rs.success,
42374                 data : data
42375             };
42376         }
42377         return Roo.decode(response.responseText);
42378     }
42379 });
42380
42381 Roo.form.Action.ACTION_TYPES = {
42382     'load' : Roo.form.Action.Load,
42383     'submit' : Roo.form.Action.Submit
42384 };/*
42385  * Based on:
42386  * Ext JS Library 1.1.1
42387  * Copyright(c) 2006-2007, Ext JS, LLC.
42388  *
42389  * Originally Released Under LGPL - original licence link has changed is not relivant.
42390  *
42391  * Fork - LGPL
42392  * <script type="text/javascript">
42393  */
42394  
42395 /**
42396  * @class Roo.form.Layout
42397  * @extends Roo.Component
42398  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42399  * @constructor
42400  * @param {Object} config Configuration options
42401  */
42402 Roo.form.Layout = function(config){
42403     var xitems = [];
42404     if (config.items) {
42405         xitems = config.items;
42406         delete config.items;
42407     }
42408     Roo.form.Layout.superclass.constructor.call(this, config);
42409     this.stack = [];
42410     Roo.each(xitems, this.addxtype, this);
42411      
42412 };
42413
42414 Roo.extend(Roo.form.Layout, Roo.Component, {
42415     /**
42416      * @cfg {String/Object} autoCreate
42417      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42418      */
42419     /**
42420      * @cfg {String/Object/Function} style
42421      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42422      * a function which returns such a specification.
42423      */
42424     /**
42425      * @cfg {String} labelAlign
42426      * Valid values are "left," "top" and "right" (defaults to "left")
42427      */
42428     /**
42429      * @cfg {Number} labelWidth
42430      * Fixed width in pixels of all field labels (defaults to undefined)
42431      */
42432     /**
42433      * @cfg {Boolean} clear
42434      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42435      */
42436     clear : true,
42437     /**
42438      * @cfg {String} labelSeparator
42439      * The separator to use after field labels (defaults to ':')
42440      */
42441     labelSeparator : ':',
42442     /**
42443      * @cfg {Boolean} hideLabels
42444      * True to suppress the display of field labels in this layout (defaults to false)
42445      */
42446     hideLabels : false,
42447
42448     // private
42449     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42450     
42451     isLayout : true,
42452     
42453     // private
42454     onRender : function(ct, position){
42455         if(this.el){ // from markup
42456             this.el = Roo.get(this.el);
42457         }else {  // generate
42458             var cfg = this.getAutoCreate();
42459             this.el = ct.createChild(cfg, position);
42460         }
42461         if(this.style){
42462             this.el.applyStyles(this.style);
42463         }
42464         if(this.labelAlign){
42465             this.el.addClass('x-form-label-'+this.labelAlign);
42466         }
42467         if(this.hideLabels){
42468             this.labelStyle = "display:none";
42469             this.elementStyle = "padding-left:0;";
42470         }else{
42471             if(typeof this.labelWidth == 'number'){
42472                 this.labelStyle = "width:"+this.labelWidth+"px;";
42473                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42474             }
42475             if(this.labelAlign == 'top'){
42476                 this.labelStyle = "width:auto;";
42477                 this.elementStyle = "padding-left:0;";
42478             }
42479         }
42480         var stack = this.stack;
42481         var slen = stack.length;
42482         if(slen > 0){
42483             if(!this.fieldTpl){
42484                 var t = new Roo.Template(
42485                     '<div class="x-form-item {5}">',
42486                         '<label for="{0}" style="{2}">{1}{4}</label>',
42487                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42488                         '</div>',
42489                     '</div><div class="x-form-clear-left"></div>'
42490                 );
42491                 t.disableFormats = true;
42492                 t.compile();
42493                 Roo.form.Layout.prototype.fieldTpl = t;
42494             }
42495             for(var i = 0; i < slen; i++) {
42496                 if(stack[i].isFormField){
42497                     this.renderField(stack[i]);
42498                 }else{
42499                     this.renderComponent(stack[i]);
42500                 }
42501             }
42502         }
42503         if(this.clear){
42504             this.el.createChild({cls:'x-form-clear'});
42505         }
42506     },
42507
42508     // private
42509     renderField : function(f){
42510         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42511                f.id, //0
42512                f.fieldLabel, //1
42513                f.labelStyle||this.labelStyle||'', //2
42514                this.elementStyle||'', //3
42515                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42516                f.itemCls||this.itemCls||''  //5
42517        ], true).getPrevSibling());
42518     },
42519
42520     // private
42521     renderComponent : function(c){
42522         c.render(c.isLayout ? this.el : this.el.createChild());    
42523     },
42524     /**
42525      * Adds a object form elements (using the xtype property as the factory method.)
42526      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42527      * @param {Object} config 
42528      */
42529     addxtype : function(o)
42530     {
42531         // create the lement.
42532         o.form = this.form;
42533         var fe = Roo.factory(o, Roo.form);
42534         this.form.allItems.push(fe);
42535         this.stack.push(fe);
42536         
42537         if (fe.isFormField) {
42538             this.form.items.add(fe);
42539         }
42540          
42541         return fe;
42542     }
42543 });
42544
42545 /**
42546  * @class Roo.form.Column
42547  * @extends Roo.form.Layout
42548  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42549  * @constructor
42550  * @param {Object} config Configuration options
42551  */
42552 Roo.form.Column = function(config){
42553     Roo.form.Column.superclass.constructor.call(this, config);
42554 };
42555
42556 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42557     /**
42558      * @cfg {Number/String} width
42559      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42560      */
42561     /**
42562      * @cfg {String/Object} autoCreate
42563      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42564      */
42565
42566     // private
42567     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42568
42569     // private
42570     onRender : function(ct, position){
42571         Roo.form.Column.superclass.onRender.call(this, ct, position);
42572         if(this.width){
42573             this.el.setWidth(this.width);
42574         }
42575     }
42576 });
42577
42578
42579 /**
42580  * @class Roo.form.Row
42581  * @extends Roo.form.Layout
42582  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42583  * @constructor
42584  * @param {Object} config Configuration options
42585  */
42586
42587  
42588 Roo.form.Row = function(config){
42589     Roo.form.Row.superclass.constructor.call(this, config);
42590 };
42591  
42592 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42593       /**
42594      * @cfg {Number/String} width
42595      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42596      */
42597     /**
42598      * @cfg {Number/String} height
42599      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42600      */
42601     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42602     
42603     padWidth : 20,
42604     // private
42605     onRender : function(ct, position){
42606         //console.log('row render');
42607         if(!this.rowTpl){
42608             var t = new Roo.Template(
42609                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42610                     '<label for="{0}" style="{2}">{1}{4}</label>',
42611                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42612                     '</div>',
42613                 '</div>'
42614             );
42615             t.disableFormats = true;
42616             t.compile();
42617             Roo.form.Layout.prototype.rowTpl = t;
42618         }
42619         this.fieldTpl = this.rowTpl;
42620         
42621         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42622         var labelWidth = 100;
42623         
42624         if ((this.labelAlign != 'top')) {
42625             if (typeof this.labelWidth == 'number') {
42626                 labelWidth = this.labelWidth
42627             }
42628             this.padWidth =  20 + labelWidth;
42629             
42630         }
42631         
42632         Roo.form.Column.superclass.onRender.call(this, ct, position);
42633         if(this.width){
42634             this.el.setWidth(this.width);
42635         }
42636         if(this.height){
42637             this.el.setHeight(this.height);
42638         }
42639     },
42640     
42641     // private
42642     renderField : function(f){
42643         f.fieldEl = this.fieldTpl.append(this.el, [
42644                f.id, f.fieldLabel,
42645                f.labelStyle||this.labelStyle||'',
42646                this.elementStyle||'',
42647                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42648                f.itemCls||this.itemCls||'',
42649                f.width ? f.width + this.padWidth : 160 + this.padWidth
42650        ],true);
42651     }
42652 });
42653  
42654
42655 /**
42656  * @class Roo.form.FieldSet
42657  * @extends Roo.form.Layout
42658  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42659  * @constructor
42660  * @param {Object} config Configuration options
42661  */
42662 Roo.form.FieldSet = function(config){
42663     Roo.form.FieldSet.superclass.constructor.call(this, config);
42664 };
42665
42666 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42667     /**
42668      * @cfg {String} legend
42669      * The text to display as the legend for the FieldSet (defaults to '')
42670      */
42671     /**
42672      * @cfg {String/Object} autoCreate
42673      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42674      */
42675
42676     // private
42677     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42678
42679     // private
42680     onRender : function(ct, position){
42681         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42682         if(this.legend){
42683             this.setLegend(this.legend);
42684         }
42685     },
42686
42687     // private
42688     setLegend : function(text){
42689         if(this.rendered){
42690             this.el.child('legend').update(text);
42691         }
42692     }
42693 });/*
42694  * Based on:
42695  * Ext JS Library 1.1.1
42696  * Copyright(c) 2006-2007, Ext JS, LLC.
42697  *
42698  * Originally Released Under LGPL - original licence link has changed is not relivant.
42699  *
42700  * Fork - LGPL
42701  * <script type="text/javascript">
42702  */
42703 /**
42704  * @class Roo.form.VTypes
42705  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42706  * @singleton
42707  */
42708 Roo.form.VTypes = function(){
42709     // closure these in so they are only created once.
42710     var alpha = /^[a-zA-Z_]+$/;
42711     var alphanum = /^[a-zA-Z0-9_]+$/;
42712     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42713     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42714
42715     // All these messages and functions are configurable
42716     return {
42717         /**
42718          * The function used to validate email addresses
42719          * @param {String} value The email address
42720          */
42721         'email' : function(v){
42722             return email.test(v);
42723         },
42724         /**
42725          * The error text to display when the email validation function returns false
42726          * @type String
42727          */
42728         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42729         /**
42730          * The keystroke filter mask to be applied on email input
42731          * @type RegExp
42732          */
42733         'emailMask' : /[a-z0-9_\.\-@]/i,
42734
42735         /**
42736          * The function used to validate URLs
42737          * @param {String} value The URL
42738          */
42739         'url' : function(v){
42740             return url.test(v);
42741         },
42742         /**
42743          * The error text to display when the url validation function returns false
42744          * @type String
42745          */
42746         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42747         
42748         /**
42749          * The function used to validate alpha values
42750          * @param {String} value The value
42751          */
42752         'alpha' : function(v){
42753             return alpha.test(v);
42754         },
42755         /**
42756          * The error text to display when the alpha validation function returns false
42757          * @type String
42758          */
42759         'alphaText' : 'This field should only contain letters and _',
42760         /**
42761          * The keystroke filter mask to be applied on alpha input
42762          * @type RegExp
42763          */
42764         'alphaMask' : /[a-z_]/i,
42765
42766         /**
42767          * The function used to validate alphanumeric values
42768          * @param {String} value The value
42769          */
42770         'alphanum' : function(v){
42771             return alphanum.test(v);
42772         },
42773         /**
42774          * The error text to display when the alphanumeric validation function returns false
42775          * @type String
42776          */
42777         'alphanumText' : 'This field should only contain letters, numbers and _',
42778         /**
42779          * The keystroke filter mask to be applied on alphanumeric input
42780          * @type RegExp
42781          */
42782         'alphanumMask' : /[a-z0-9_]/i
42783     };
42784 }();//<script type="text/javascript">
42785
42786 /**
42787  * @class Roo.form.FCKeditor
42788  * @extends Roo.form.TextArea
42789  * Wrapper around the FCKEditor http://www.fckeditor.net
42790  * @constructor
42791  * Creates a new FCKeditor
42792  * @param {Object} config Configuration options
42793  */
42794 Roo.form.FCKeditor = function(config){
42795     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42796     this.addEvents({
42797          /**
42798          * @event editorinit
42799          * Fired when the editor is initialized - you can add extra handlers here..
42800          * @param {FCKeditor} this
42801          * @param {Object} the FCK object.
42802          */
42803         editorinit : true
42804     });
42805     
42806     
42807 };
42808 Roo.form.FCKeditor.editors = { };
42809 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42810 {
42811     //defaultAutoCreate : {
42812     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42813     //},
42814     // private
42815     /**
42816      * @cfg {Object} fck options - see fck manual for details.
42817      */
42818     fckconfig : false,
42819     
42820     /**
42821      * @cfg {Object} fck toolbar set (Basic or Default)
42822      */
42823     toolbarSet : 'Basic',
42824     /**
42825      * @cfg {Object} fck BasePath
42826      */ 
42827     basePath : '/fckeditor/',
42828     
42829     
42830     frame : false,
42831     
42832     value : '',
42833     
42834    
42835     onRender : function(ct, position)
42836     {
42837         if(!this.el){
42838             this.defaultAutoCreate = {
42839                 tag: "textarea",
42840                 style:"width:300px;height:60px;",
42841                 autocomplete: "off"
42842             };
42843         }
42844         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42845         /*
42846         if(this.grow){
42847             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42848             if(this.preventScrollbars){
42849                 this.el.setStyle("overflow", "hidden");
42850             }
42851             this.el.setHeight(this.growMin);
42852         }
42853         */
42854         //console.log('onrender' + this.getId() );
42855         Roo.form.FCKeditor.editors[this.getId()] = this;
42856          
42857
42858         this.replaceTextarea() ;
42859         
42860     },
42861     
42862     getEditor : function() {
42863         return this.fckEditor;
42864     },
42865     /**
42866      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42867      * @param {Mixed} value The value to set
42868      */
42869     
42870     
42871     setValue : function(value)
42872     {
42873         //console.log('setValue: ' + value);
42874         
42875         if(typeof(value) == 'undefined') { // not sure why this is happending...
42876             return;
42877         }
42878         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42879         
42880         //if(!this.el || !this.getEditor()) {
42881         //    this.value = value;
42882             //this.setValue.defer(100,this,[value]);    
42883         //    return;
42884         //} 
42885         
42886         if(!this.getEditor()) {
42887             return;
42888         }
42889         
42890         this.getEditor().SetData(value);
42891         
42892         //
42893
42894     },
42895
42896     /**
42897      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42898      * @return {Mixed} value The field value
42899      */
42900     getValue : function()
42901     {
42902         
42903         if (this.frame && this.frame.dom.style.display == 'none') {
42904             return Roo.form.FCKeditor.superclass.getValue.call(this);
42905         }
42906         
42907         if(!this.el || !this.getEditor()) {
42908            
42909            // this.getValue.defer(100,this); 
42910             return this.value;
42911         }
42912        
42913         
42914         var value=this.getEditor().GetData();
42915         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42916         return Roo.form.FCKeditor.superclass.getValue.call(this);
42917         
42918
42919     },
42920
42921     /**
42922      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42923      * @return {Mixed} value The field value
42924      */
42925     getRawValue : function()
42926     {
42927         if (this.frame && this.frame.dom.style.display == 'none') {
42928             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42929         }
42930         
42931         if(!this.el || !this.getEditor()) {
42932             //this.getRawValue.defer(100,this); 
42933             return this.value;
42934             return;
42935         }
42936         
42937         
42938         
42939         var value=this.getEditor().GetData();
42940         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42941         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42942          
42943     },
42944     
42945     setSize : function(w,h) {
42946         
42947         
42948         
42949         //if (this.frame && this.frame.dom.style.display == 'none') {
42950         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42951         //    return;
42952         //}
42953         //if(!this.el || !this.getEditor()) {
42954         //    this.setSize.defer(100,this, [w,h]); 
42955         //    return;
42956         //}
42957         
42958         
42959         
42960         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42961         
42962         this.frame.dom.setAttribute('width', w);
42963         this.frame.dom.setAttribute('height', h);
42964         this.frame.setSize(w,h);
42965         
42966     },
42967     
42968     toggleSourceEdit : function(value) {
42969         
42970       
42971          
42972         this.el.dom.style.display = value ? '' : 'none';
42973         this.frame.dom.style.display = value ?  'none' : '';
42974         
42975     },
42976     
42977     
42978     focus: function(tag)
42979     {
42980         if (this.frame.dom.style.display == 'none') {
42981             return Roo.form.FCKeditor.superclass.focus.call(this);
42982         }
42983         if(!this.el || !this.getEditor()) {
42984             this.focus.defer(100,this, [tag]); 
42985             return;
42986         }
42987         
42988         
42989         
42990         
42991         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42992         this.getEditor().Focus();
42993         if (tgs.length) {
42994             if (!this.getEditor().Selection.GetSelection()) {
42995                 this.focus.defer(100,this, [tag]); 
42996                 return;
42997             }
42998             
42999             
43000             var r = this.getEditor().EditorDocument.createRange();
43001             r.setStart(tgs[0],0);
43002             r.setEnd(tgs[0],0);
43003             this.getEditor().Selection.GetSelection().removeAllRanges();
43004             this.getEditor().Selection.GetSelection().addRange(r);
43005             this.getEditor().Focus();
43006         }
43007         
43008     },
43009     
43010     
43011     
43012     replaceTextarea : function()
43013     {
43014         if ( document.getElementById( this.getId() + '___Frame' ) )
43015             return ;
43016         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43017         //{
43018             // We must check the elements firstly using the Id and then the name.
43019         var oTextarea = document.getElementById( this.getId() );
43020         
43021         var colElementsByName = document.getElementsByName( this.getId() ) ;
43022          
43023         oTextarea.style.display = 'none' ;
43024
43025         if ( oTextarea.tabIndex ) {            
43026             this.TabIndex = oTextarea.tabIndex ;
43027         }
43028         
43029         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43030         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43031         this.frame = Roo.get(this.getId() + '___Frame')
43032     },
43033     
43034     _getConfigHtml : function()
43035     {
43036         var sConfig = '' ;
43037
43038         for ( var o in this.fckconfig ) {
43039             sConfig += sConfig.length > 0  ? '&amp;' : '';
43040             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43041         }
43042
43043         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43044     },
43045     
43046     
43047     _getIFrameHtml : function()
43048     {
43049         var sFile = 'fckeditor.html' ;
43050         /* no idea what this is about..
43051         try
43052         {
43053             if ( (/fcksource=true/i).test( window.top.location.search ) )
43054                 sFile = 'fckeditor.original.html' ;
43055         }
43056         catch (e) { 
43057         */
43058
43059         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43060         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43061         
43062         
43063         var html = '<iframe id="' + this.getId() +
43064             '___Frame" src="' + sLink +
43065             '" width="' + this.width +
43066             '" height="' + this.height + '"' +
43067             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43068             ' frameborder="0" scrolling="no"></iframe>' ;
43069
43070         return html ;
43071     },
43072     
43073     _insertHtmlBefore : function( html, element )
43074     {
43075         if ( element.insertAdjacentHTML )       {
43076             // IE
43077             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43078         } else { // Gecko
43079             var oRange = document.createRange() ;
43080             oRange.setStartBefore( element ) ;
43081             var oFragment = oRange.createContextualFragment( html );
43082             element.parentNode.insertBefore( oFragment, element ) ;
43083         }
43084     }
43085     
43086     
43087   
43088     
43089     
43090     
43091     
43092
43093 });
43094
43095 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43096
43097 function FCKeditor_OnComplete(editorInstance){
43098     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43099     f.fckEditor = editorInstance;
43100     //console.log("loaded");
43101     f.fireEvent('editorinit', f, editorInstance);
43102
43103   
43104
43105  
43106
43107
43108
43109
43110
43111
43112
43113
43114
43115
43116
43117
43118
43119
43120
43121 //<script type="text/javascript">
43122 /**
43123  * @class Roo.form.GridField
43124  * @extends Roo.form.Field
43125  * Embed a grid (or editable grid into a form)
43126  * STATUS ALPHA
43127  * 
43128  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43129  * it needs 
43130  * xgrid.store = Roo.data.Store
43131  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43132  * xgrid.store.reader = Roo.data.JsonReader 
43133  * 
43134  * 
43135  * @constructor
43136  * Creates a new GridField
43137  * @param {Object} config Configuration options
43138  */
43139 Roo.form.GridField = function(config){
43140     Roo.form.GridField.superclass.constructor.call(this, config);
43141      
43142 };
43143
43144 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43145     /**
43146      * @cfg {Number} width  - used to restrict width of grid..
43147      */
43148     width : 100,
43149     /**
43150      * @cfg {Number} height - used to restrict height of grid..
43151      */
43152     height : 50,
43153      /**
43154      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43155          * 
43156          *}
43157      */
43158     xgrid : false, 
43159     /**
43160      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43161      * {tag: "input", type: "checkbox", autocomplete: "off"})
43162      */
43163    // defaultAutoCreate : { tag: 'div' },
43164     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43165     /**
43166      * @cfg {String} addTitle Text to include for adding a title.
43167      */
43168     addTitle : false,
43169     //
43170     onResize : function(){
43171         Roo.form.Field.superclass.onResize.apply(this, arguments);
43172     },
43173
43174     initEvents : function(){
43175         // Roo.form.Checkbox.superclass.initEvents.call(this);
43176         // has no events...
43177        
43178     },
43179
43180
43181     getResizeEl : function(){
43182         return this.wrap;
43183     },
43184
43185     getPositionEl : function(){
43186         return this.wrap;
43187     },
43188
43189     // private
43190     onRender : function(ct, position){
43191         
43192         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43193         var style = this.style;
43194         delete this.style;
43195         
43196         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43197         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43198         this.viewEl = this.wrap.createChild({ tag: 'div' });
43199         if (style) {
43200             this.viewEl.applyStyles(style);
43201         }
43202         if (this.width) {
43203             this.viewEl.setWidth(this.width);
43204         }
43205         if (this.height) {
43206             this.viewEl.setHeight(this.height);
43207         }
43208         //if(this.inputValue !== undefined){
43209         //this.setValue(this.value);
43210         
43211         
43212         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43213         
43214         
43215         this.grid.render();
43216         this.grid.getDataSource().on('remove', this.refreshValue, this);
43217         this.grid.getDataSource().on('update', this.refreshValue, this);
43218         this.grid.on('afteredit', this.refreshValue, this);
43219  
43220     },
43221      
43222     
43223     /**
43224      * Sets the value of the item. 
43225      * @param {String} either an object  or a string..
43226      */
43227     setValue : function(v){
43228         //this.value = v;
43229         v = v || []; // empty set..
43230         // this does not seem smart - it really only affects memoryproxy grids..
43231         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43232             var ds = this.grid.getDataSource();
43233             // assumes a json reader..
43234             var data = {}
43235             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43236             ds.loadData( data);
43237         }
43238         // clear selection so it does not get stale.
43239         if (this.grid.sm) { 
43240             this.grid.sm.clearSelections();
43241         }
43242         
43243         Roo.form.GridField.superclass.setValue.call(this, v);
43244         this.refreshValue();
43245         // should load data in the grid really....
43246     },
43247     
43248     // private
43249     refreshValue: function() {
43250          var val = [];
43251         this.grid.getDataSource().each(function(r) {
43252             val.push(r.data);
43253         });
43254         this.el.dom.value = Roo.encode(val);
43255     }
43256     
43257      
43258     
43259     
43260 });/*
43261  * Based on:
43262  * Ext JS Library 1.1.1
43263  * Copyright(c) 2006-2007, Ext JS, LLC.
43264  *
43265  * Originally Released Under LGPL - original licence link has changed is not relivant.
43266  *
43267  * Fork - LGPL
43268  * <script type="text/javascript">
43269  */
43270 /**
43271  * @class Roo.form.DisplayField
43272  * @extends Roo.form.Field
43273  * A generic Field to display non-editable data.
43274  * @constructor
43275  * Creates a new Display Field item.
43276  * @param {Object} config Configuration options
43277  */
43278 Roo.form.DisplayField = function(config){
43279     Roo.form.DisplayField.superclass.constructor.call(this, config);
43280     
43281 };
43282
43283 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43284     inputType:      'hidden',
43285     allowBlank:     true,
43286     readOnly:         true,
43287     
43288  
43289     /**
43290      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43291      */
43292     focusClass : undefined,
43293     /**
43294      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43295      */
43296     fieldClass: 'x-form-field',
43297     
43298      /**
43299      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43300      */
43301     valueRenderer: undefined,
43302     
43303     width: 100,
43304     /**
43305      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43306      * {tag: "input", type: "checkbox", autocomplete: "off"})
43307      */
43308      
43309  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43310
43311     onResize : function(){
43312         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43313         
43314     },
43315
43316     initEvents : function(){
43317         // Roo.form.Checkbox.superclass.initEvents.call(this);
43318         // has no events...
43319        
43320     },
43321
43322
43323     getResizeEl : function(){
43324         return this.wrap;
43325     },
43326
43327     getPositionEl : function(){
43328         return this.wrap;
43329     },
43330
43331     // private
43332     onRender : function(ct, position){
43333         
43334         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43335         //if(this.inputValue !== undefined){
43336         this.wrap = this.el.wrap();
43337         
43338         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43339         
43340         if (this.bodyStyle) {
43341             this.viewEl.applyStyles(this.bodyStyle);
43342         }
43343         //this.viewEl.setStyle('padding', '2px');
43344         
43345         this.setValue(this.value);
43346         
43347     },
43348 /*
43349     // private
43350     initValue : Roo.emptyFn,
43351
43352   */
43353
43354         // private
43355     onClick : function(){
43356         
43357     },
43358
43359     /**
43360      * Sets the checked state of the checkbox.
43361      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43362      */
43363     setValue : function(v){
43364         this.value = v;
43365         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43366         // this might be called before we have a dom element..
43367         if (!this.viewEl) {
43368             return;
43369         }
43370         this.viewEl.dom.innerHTML = html;
43371         Roo.form.DisplayField.superclass.setValue.call(this, v);
43372
43373     }
43374 });/*
43375  * 
43376  * Licence- LGPL
43377  * 
43378  */
43379
43380 /**
43381  * @class Roo.form.DayPicker
43382  * @extends Roo.form.Field
43383  * A Day picker show [M] [T] [W] ....
43384  * @constructor
43385  * Creates a new Day Picker
43386  * @param {Object} config Configuration options
43387  */
43388 Roo.form.DayPicker= function(config){
43389     Roo.form.DayPicker.superclass.constructor.call(this, config);
43390      
43391 };
43392
43393 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43394     /**
43395      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43396      */
43397     focusClass : undefined,
43398     /**
43399      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43400      */
43401     fieldClass: "x-form-field",
43402    
43403     /**
43404      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43405      * {tag: "input", type: "checkbox", autocomplete: "off"})
43406      */
43407     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43408     
43409    
43410     actionMode : 'viewEl', 
43411     //
43412     // private
43413  
43414     inputType : 'hidden',
43415     
43416      
43417     inputElement: false, // real input element?
43418     basedOn: false, // ????
43419     
43420     isFormField: true, // not sure where this is needed!!!!
43421
43422     onResize : function(){
43423         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43424         if(!this.boxLabel){
43425             this.el.alignTo(this.wrap, 'c-c');
43426         }
43427     },
43428
43429     initEvents : function(){
43430         Roo.form.Checkbox.superclass.initEvents.call(this);
43431         this.el.on("click", this.onClick,  this);
43432         this.el.on("change", this.onClick,  this);
43433     },
43434
43435
43436     getResizeEl : function(){
43437         return this.wrap;
43438     },
43439
43440     getPositionEl : function(){
43441         return this.wrap;
43442     },
43443
43444     
43445     // private
43446     onRender : function(ct, position){
43447         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43448        
43449         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43450         
43451         var r1 = '<table><tr>';
43452         var r2 = '<tr class="x-form-daypick-icons">';
43453         for (var i=0; i < 7; i++) {
43454             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43455             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43456         }
43457         
43458         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43459         viewEl.select('img').on('click', this.onClick, this);
43460         this.viewEl = viewEl;   
43461         
43462         
43463         // this will not work on Chrome!!!
43464         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43465         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43466         
43467         
43468           
43469
43470     },
43471
43472     // private
43473     initValue : Roo.emptyFn,
43474
43475     /**
43476      * Returns the checked state of the checkbox.
43477      * @return {Boolean} True if checked, else false
43478      */
43479     getValue : function(){
43480         return this.el.dom.value;
43481         
43482     },
43483
43484         // private
43485     onClick : function(e){ 
43486         //this.setChecked(!this.checked);
43487         Roo.get(e.target).toggleClass('x-menu-item-checked');
43488         this.refreshValue();
43489         //if(this.el.dom.checked != this.checked){
43490         //    this.setValue(this.el.dom.checked);
43491        // }
43492     },
43493     
43494     // private
43495     refreshValue : function()
43496     {
43497         var val = '';
43498         this.viewEl.select('img',true).each(function(e,i,n)  {
43499             val += e.is(".x-menu-item-checked") ? String(n) : '';
43500         });
43501         this.setValue(val, true);
43502     },
43503
43504     /**
43505      * Sets the checked state of the checkbox.
43506      * On is always based on a string comparison between inputValue and the param.
43507      * @param {Boolean/String} value - the value to set 
43508      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43509      */
43510     setValue : function(v,suppressEvent){
43511         if (!this.el.dom) {
43512             return;
43513         }
43514         var old = this.el.dom.value ;
43515         this.el.dom.value = v;
43516         if (suppressEvent) {
43517             return ;
43518         }
43519          
43520         // update display..
43521         this.viewEl.select('img',true).each(function(e,i,n)  {
43522             
43523             var on = e.is(".x-menu-item-checked");
43524             var newv = v.indexOf(String(n)) > -1;
43525             if (on != newv) {
43526                 e.toggleClass('x-menu-item-checked');
43527             }
43528             
43529         });
43530         
43531         
43532         this.fireEvent('change', this, v, old);
43533         
43534         
43535     },
43536    
43537     // handle setting of hidden value by some other method!!?!?
43538     setFromHidden: function()
43539     {
43540         if(!this.el){
43541             return;
43542         }
43543         //console.log("SET FROM HIDDEN");
43544         //alert('setFrom hidden');
43545         this.setValue(this.el.dom.value);
43546     },
43547     
43548     onDestroy : function()
43549     {
43550         if(this.viewEl){
43551             Roo.get(this.viewEl).remove();
43552         }
43553          
43554         Roo.form.DayPicker.superclass.onDestroy.call(this);
43555     }
43556
43557 });/*
43558  * RooJS Library 1.1.1
43559  * Copyright(c) 2008-2011  Alan Knowles
43560  *
43561  * License - LGPL
43562  */
43563  
43564
43565 /**
43566  * @class Roo.form.ComboCheck
43567  * @extends Roo.form.ComboBox
43568  * A combobox for multiple select items.
43569  *
43570  * FIXME - could do with a reset button..
43571  * 
43572  * @constructor
43573  * Create a new ComboCheck
43574  * @param {Object} config Configuration options
43575  */
43576 Roo.form.ComboCheck = function(config){
43577     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43578     // should verify some data...
43579     // like
43580     // hiddenName = required..
43581     // displayField = required
43582     // valudField == required
43583     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43584     var _t = this;
43585     Roo.each(req, function(e) {
43586         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43587             throw "Roo.form.ComboCheck : missing value for: " + e;
43588         }
43589     });
43590     
43591     
43592 };
43593
43594 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43595      
43596      
43597     editable : false,
43598      
43599     selectedClass: 'x-menu-item-checked', 
43600     
43601     // private
43602     onRender : function(ct, position){
43603         var _t = this;
43604         
43605         
43606         
43607         if(!this.tpl){
43608             var cls = 'x-combo-list';
43609
43610             
43611             this.tpl =  new Roo.Template({
43612                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43613                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43614                    '<span>{' + this.displayField + '}</span>' +
43615                     '</div>' 
43616                 
43617             });
43618         }
43619  
43620         
43621         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43622         this.view.singleSelect = false;
43623         this.view.multiSelect = true;
43624         this.view.toggleSelect = true;
43625         this.pageTb.add(new Roo.Toolbar.Fill(), {
43626             
43627             text: 'Done',
43628             handler: function()
43629             {
43630                 _t.collapse();
43631             }
43632         });
43633     },
43634     
43635     onViewOver : function(e, t){
43636         // do nothing...
43637         return;
43638         
43639     },
43640     
43641     onViewClick : function(doFocus,index){
43642         return;
43643         
43644     },
43645     select: function () {
43646         //Roo.log("SELECT CALLED");
43647     },
43648      
43649     selectByValue : function(xv, scrollIntoView){
43650         var ar = this.getValueArray();
43651         var sels = [];
43652         
43653         Roo.each(ar, function(v) {
43654             if(v === undefined || v === null){
43655                 return;
43656             }
43657             var r = this.findRecord(this.valueField, v);
43658             if(r){
43659                 sels.push(this.store.indexOf(r))
43660                 
43661             }
43662         },this);
43663         this.view.select(sels);
43664         return false;
43665     },
43666     
43667     
43668     
43669     onSelect : function(record, index){
43670        // Roo.log("onselect Called");
43671        // this is only called by the clear button now..
43672         this.view.clearSelections();
43673         this.setValue('[]');
43674         if (this.value != this.valueBefore) {
43675             this.fireEvent('change', this, this.value, this.valueBefore);
43676         }
43677     },
43678     getValueArray : function()
43679     {
43680         var ar = [] ;
43681         
43682         try {
43683             Roo.log(this.value);
43684             var ar = Roo.decode(this.value);
43685             return  ar instanceof Array ? ar : []; //?? valid?
43686             
43687         } catch(e) {
43688             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43689             return [];
43690         }
43691          
43692     },
43693     expand : function ()
43694     {
43695         Roo.form.ComboCheck.superclass.expand.call(this);
43696         this.valueBefore = this.value;
43697         
43698
43699     },
43700     
43701     collapse : function(){
43702         Roo.form.ComboCheck.superclass.collapse.call(this);
43703         var sl = this.view.getSelectedIndexes();
43704         var st = this.store;
43705         var nv = [];
43706         var tv = [];
43707         var r;
43708         Roo.each(sl, function(i) {
43709             r = st.getAt(i);
43710             nv.push(r.get(this.valueField));
43711         },this);
43712         this.setValue(Roo.encode(nv));
43713         if (this.value != this.valueBefore) {
43714
43715             this.fireEvent('change', this, this.value, this.valueBefore);
43716         }
43717         
43718     },
43719     
43720     setValue : function(v){
43721         // Roo.log(v);
43722         this.value = v;
43723         
43724         var vals = this.getValueArray();
43725         var tv = [];
43726         Roo.each(vals, function(k) {
43727             var r = this.findRecord(this.valueField, k);
43728             if(r){
43729                 tv.push(r.data[this.displayField]);
43730             }else if(this.valueNotFoundText !== undefined){
43731                 tv.push( this.valueNotFoundText );
43732             }
43733         },this);
43734        // Roo.log(tv);
43735         
43736         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43737         this.hiddenField.value = v;
43738         this.value = v;
43739     }
43740     
43741 });//<script type="text/javasscript">
43742  
43743
43744 /**
43745  * @class Roo.DDView
43746  * A DnD enabled version of Roo.View.
43747  * @param {Element/String} container The Element in which to create the View.
43748  * @param {String} tpl The template string used to create the markup for each element of the View
43749  * @param {Object} config The configuration properties. These include all the config options of
43750  * {@link Roo.View} plus some specific to this class.<br>
43751  * <p>
43752  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43753  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43754  * <p>
43755  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43756 .x-view-drag-insert-above {
43757         border-top:1px dotted #3366cc;
43758 }
43759 .x-view-drag-insert-below {
43760         border-bottom:1px dotted #3366cc;
43761 }
43762 </code></pre>
43763  * 
43764  */
43765  
43766 Roo.DDView = function(container, tpl, config) {
43767     Roo.DDView.superclass.constructor.apply(this, arguments);
43768     this.getEl().setStyle("outline", "0px none");
43769     this.getEl().unselectable();
43770     if (this.dragGroup) {
43771                 this.setDraggable(this.dragGroup.split(","));
43772     }
43773     if (this.dropGroup) {
43774                 this.setDroppable(this.dropGroup.split(","));
43775     }
43776     if (this.deletable) {
43777         this.setDeletable();
43778     }
43779     this.isDirtyFlag = false;
43780         this.addEvents({
43781                 "drop" : true
43782         });
43783 };
43784
43785 Roo.extend(Roo.DDView, Roo.View, {
43786 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43787 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43788 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43789 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43790
43791         isFormField: true,
43792
43793         reset: Roo.emptyFn,
43794         
43795         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43796
43797         validate: function() {
43798                 return true;
43799         },
43800         
43801         destroy: function() {
43802                 this.purgeListeners();
43803                 this.getEl.removeAllListeners();
43804                 this.getEl().remove();
43805                 if (this.dragZone) {
43806                         if (this.dragZone.destroy) {
43807                                 this.dragZone.destroy();
43808                         }
43809                 }
43810                 if (this.dropZone) {
43811                         if (this.dropZone.destroy) {
43812                                 this.dropZone.destroy();
43813                         }
43814                 }
43815         },
43816
43817 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43818         getName: function() {
43819                 return this.name;
43820         },
43821
43822 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43823         setValue: function(v) {
43824                 if (!this.store) {
43825                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43826                 }
43827                 var data = {};
43828                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43829                 this.store.proxy = new Roo.data.MemoryProxy(data);
43830                 this.store.load();
43831         },
43832
43833 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43834         getValue: function() {
43835                 var result = '(';
43836                 this.store.each(function(rec) {
43837                         result += rec.id + ',';
43838                 });
43839                 return result.substr(0, result.length - 1) + ')';
43840         },
43841         
43842         getIds: function() {
43843                 var i = 0, result = new Array(this.store.getCount());
43844                 this.store.each(function(rec) {
43845                         result[i++] = rec.id;
43846                 });
43847                 return result;
43848         },
43849         
43850         isDirty: function() {
43851                 return this.isDirtyFlag;
43852         },
43853
43854 /**
43855  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43856  *      whole Element becomes the target, and this causes the drop gesture to append.
43857  */
43858     getTargetFromEvent : function(e) {
43859                 var target = e.getTarget();
43860                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43861                 target = target.parentNode;
43862                 }
43863                 if (!target) {
43864                         target = this.el.dom.lastChild || this.el.dom;
43865                 }
43866                 return target;
43867     },
43868
43869 /**
43870  *      Create the drag data which consists of an object which has the property "ddel" as
43871  *      the drag proxy element. 
43872  */
43873     getDragData : function(e) {
43874         var target = this.findItemFromChild(e.getTarget());
43875                 if(target) {
43876                         this.handleSelection(e);
43877                         var selNodes = this.getSelectedNodes();
43878             var dragData = {
43879                 source: this,
43880                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43881                 nodes: selNodes,
43882                 records: []
43883                         };
43884                         var selectedIndices = this.getSelectedIndexes();
43885                         for (var i = 0; i < selectedIndices.length; i++) {
43886                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43887                         }
43888                         if (selNodes.length == 1) {
43889                                 dragData.ddel = target.cloneNode(true); // the div element
43890                         } else {
43891                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43892                                 div.className = 'multi-proxy';
43893                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43894                                         div.appendChild(selNodes[i].cloneNode(true));
43895                                 }
43896                                 dragData.ddel = div;
43897                         }
43898             //console.log(dragData)
43899             //console.log(dragData.ddel.innerHTML)
43900                         return dragData;
43901                 }
43902         //console.log('nodragData')
43903                 return false;
43904     },
43905     
43906 /**     Specify to which ddGroup items in this DDView may be dragged. */
43907     setDraggable: function(ddGroup) {
43908         if (ddGroup instanceof Array) {
43909                 Roo.each(ddGroup, this.setDraggable, this);
43910                 return;
43911         }
43912         if (this.dragZone) {
43913                 this.dragZone.addToGroup(ddGroup);
43914         } else {
43915                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43916                                 containerScroll: true,
43917                                 ddGroup: ddGroup 
43918
43919                         });
43920 //                      Draggability implies selection. DragZone's mousedown selects the element.
43921                         if (!this.multiSelect) { this.singleSelect = true; }
43922
43923 //                      Wire the DragZone's handlers up to methods in *this*
43924                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43925                 }
43926     },
43927
43928 /**     Specify from which ddGroup this DDView accepts drops. */
43929     setDroppable: function(ddGroup) {
43930         if (ddGroup instanceof Array) {
43931                 Roo.each(ddGroup, this.setDroppable, this);
43932                 return;
43933         }
43934         if (this.dropZone) {
43935                 this.dropZone.addToGroup(ddGroup);
43936         } else {
43937                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43938                                 containerScroll: true,
43939                                 ddGroup: ddGroup
43940                         });
43941
43942 //                      Wire the DropZone's handlers up to methods in *this*
43943                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43944                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43945                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43946                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43947                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43948                 }
43949     },
43950
43951 /**     Decide whether to drop above or below a View node. */
43952     getDropPoint : function(e, n, dd){
43953         if (n == this.el.dom) { return "above"; }
43954                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43955                 var c = t + (b - t) / 2;
43956                 var y = Roo.lib.Event.getPageY(e);
43957                 if(y <= c) {
43958                         return "above";
43959                 }else{
43960                         return "below";
43961                 }
43962     },
43963
43964     onNodeEnter : function(n, dd, e, data){
43965                 return false;
43966     },
43967     
43968     onNodeOver : function(n, dd, e, data){
43969                 var pt = this.getDropPoint(e, n, dd);
43970                 // set the insert point style on the target node
43971                 var dragElClass = this.dropNotAllowed;
43972                 if (pt) {
43973                         var targetElClass;
43974                         if (pt == "above"){
43975                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43976                                 targetElClass = "x-view-drag-insert-above";
43977                         } else {
43978                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43979                                 targetElClass = "x-view-drag-insert-below";
43980                         }
43981                         if (this.lastInsertClass != targetElClass){
43982                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43983                                 this.lastInsertClass = targetElClass;
43984                         }
43985                 }
43986                 return dragElClass;
43987         },
43988
43989     onNodeOut : function(n, dd, e, data){
43990                 this.removeDropIndicators(n);
43991     },
43992
43993     onNodeDrop : function(n, dd, e, data){
43994         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43995                 return false;
43996         }
43997         var pt = this.getDropPoint(e, n, dd);
43998                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43999                 if (pt == "below") { insertAt++; }
44000                 for (var i = 0; i < data.records.length; i++) {
44001                         var r = data.records[i];
44002                         var dup = this.store.getById(r.id);
44003                         if (dup && (dd != this.dragZone)) {
44004                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44005                         } else {
44006                                 if (data.copy) {
44007                                         this.store.insert(insertAt++, r.copy());
44008                                 } else {
44009                                         data.source.isDirtyFlag = true;
44010                                         r.store.remove(r);
44011                                         this.store.insert(insertAt++, r);
44012                                 }
44013                                 this.isDirtyFlag = true;
44014                         }
44015                 }
44016                 this.dragZone.cachedTarget = null;
44017                 return true;
44018     },
44019
44020     removeDropIndicators : function(n){
44021                 if(n){
44022                         Roo.fly(n).removeClass([
44023                                 "x-view-drag-insert-above",
44024                                 "x-view-drag-insert-below"]);
44025                         this.lastInsertClass = "_noclass";
44026                 }
44027     },
44028
44029 /**
44030  *      Utility method. Add a delete option to the DDView's context menu.
44031  *      @param {String} imageUrl The URL of the "delete" icon image.
44032  */
44033         setDeletable: function(imageUrl) {
44034                 if (!this.singleSelect && !this.multiSelect) {
44035                         this.singleSelect = true;
44036                 }
44037                 var c = this.getContextMenu();
44038                 this.contextMenu.on("itemclick", function(item) {
44039                         switch (item.id) {
44040                                 case "delete":
44041                                         this.remove(this.getSelectedIndexes());
44042                                         break;
44043                         }
44044                 }, this);
44045                 this.contextMenu.add({
44046                         icon: imageUrl,
44047                         id: "delete",
44048                         text: 'Delete'
44049                 });
44050         },
44051         
44052 /**     Return the context menu for this DDView. */
44053         getContextMenu: function() {
44054                 if (!this.contextMenu) {
44055 //                      Create the View's context menu
44056                         this.contextMenu = new Roo.menu.Menu({
44057                                 id: this.id + "-contextmenu"
44058                         });
44059                         this.el.on("contextmenu", this.showContextMenu, this);
44060                 }
44061                 return this.contextMenu;
44062         },
44063         
44064         disableContextMenu: function() {
44065                 if (this.contextMenu) {
44066                         this.el.un("contextmenu", this.showContextMenu, this);
44067                 }
44068         },
44069
44070         showContextMenu: function(e, item) {
44071         item = this.findItemFromChild(e.getTarget());
44072                 if (item) {
44073                         e.stopEvent();
44074                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44075                         this.contextMenu.showAt(e.getXY());
44076             }
44077     },
44078
44079 /**
44080  *      Remove {@link Roo.data.Record}s at the specified indices.
44081  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44082  */
44083     remove: function(selectedIndices) {
44084                 selectedIndices = [].concat(selectedIndices);
44085                 for (var i = 0; i < selectedIndices.length; i++) {
44086                         var rec = this.store.getAt(selectedIndices[i]);
44087                         this.store.remove(rec);
44088                 }
44089     },
44090
44091 /**
44092  *      Double click fires the event, but also, if this is draggable, and there is only one other
44093  *      related DropZone, it transfers the selected node.
44094  */
44095     onDblClick : function(e){
44096         var item = this.findItemFromChild(e.getTarget());
44097         if(item){
44098             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44099                 return false;
44100             }
44101             if (this.dragGroup) {
44102                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44103                     while (targets.indexOf(this.dropZone) > -1) {
44104                             targets.remove(this.dropZone);
44105                                 }
44106                     if (targets.length == 1) {
44107                                         this.dragZone.cachedTarget = null;
44108                         var el = Roo.get(targets[0].getEl());
44109                         var box = el.getBox(true);
44110                         targets[0].onNodeDrop(el.dom, {
44111                                 target: el.dom,
44112                                 xy: [box.x, box.y + box.height - 1]
44113                         }, null, this.getDragData(e));
44114                     }
44115                 }
44116         }
44117     },
44118     
44119     handleSelection: function(e) {
44120                 this.dragZone.cachedTarget = null;
44121         var item = this.findItemFromChild(e.getTarget());
44122         if (!item) {
44123                 this.clearSelections(true);
44124                 return;
44125         }
44126                 if (item && (this.multiSelect || this.singleSelect)){
44127                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44128                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44129                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44130                                 this.unselect(item);
44131                         } else {
44132                                 this.select(item, this.multiSelect && e.ctrlKey);
44133                                 this.lastSelection = item;
44134                         }
44135                 }
44136     },
44137
44138     onItemClick : function(item, index, e){
44139                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44140                         return false;
44141                 }
44142                 return true;
44143     },
44144
44145     unselect : function(nodeInfo, suppressEvent){
44146                 var node = this.getNode(nodeInfo);
44147                 if(node && this.isSelected(node)){
44148                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44149                                 Roo.fly(node).removeClass(this.selectedClass);
44150                                 this.selections.remove(node);
44151                                 if(!suppressEvent){
44152                                         this.fireEvent("selectionchange", this, this.selections);
44153                                 }
44154                         }
44155                 }
44156     }
44157 });
44158 /*
44159  * Based on:
44160  * Ext JS Library 1.1.1
44161  * Copyright(c) 2006-2007, Ext JS, LLC.
44162  *
44163  * Originally Released Under LGPL - original licence link has changed is not relivant.
44164  *
44165  * Fork - LGPL
44166  * <script type="text/javascript">
44167  */
44168  
44169 /**
44170  * @class Roo.LayoutManager
44171  * @extends Roo.util.Observable
44172  * Base class for layout managers.
44173  */
44174 Roo.LayoutManager = function(container, config){
44175     Roo.LayoutManager.superclass.constructor.call(this);
44176     this.el = Roo.get(container);
44177     // ie scrollbar fix
44178     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44179         document.body.scroll = "no";
44180     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44181         this.el.position('relative');
44182     }
44183     this.id = this.el.id;
44184     this.el.addClass("x-layout-container");
44185     /** false to disable window resize monitoring @type Boolean */
44186     this.monitorWindowResize = true;
44187     this.regions = {};
44188     this.addEvents({
44189         /**
44190          * @event layout
44191          * Fires when a layout is performed. 
44192          * @param {Roo.LayoutManager} this
44193          */
44194         "layout" : true,
44195         /**
44196          * @event regionresized
44197          * Fires when the user resizes a region. 
44198          * @param {Roo.LayoutRegion} region The resized region
44199          * @param {Number} newSize The new size (width for east/west, height for north/south)
44200          */
44201         "regionresized" : true,
44202         /**
44203          * @event regioncollapsed
44204          * Fires when a region is collapsed. 
44205          * @param {Roo.LayoutRegion} region The collapsed region
44206          */
44207         "regioncollapsed" : true,
44208         /**
44209          * @event regionexpanded
44210          * Fires when a region is expanded.  
44211          * @param {Roo.LayoutRegion} region The expanded region
44212          */
44213         "regionexpanded" : true
44214     });
44215     this.updating = false;
44216     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44217 };
44218
44219 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44220     /**
44221      * Returns true if this layout is currently being updated
44222      * @return {Boolean}
44223      */
44224     isUpdating : function(){
44225         return this.updating; 
44226     },
44227     
44228     /**
44229      * Suspend the LayoutManager from doing auto-layouts while
44230      * making multiple add or remove calls
44231      */
44232     beginUpdate : function(){
44233         this.updating = true;    
44234     },
44235     
44236     /**
44237      * Restore auto-layouts and optionally disable the manager from performing a layout
44238      * @param {Boolean} noLayout true to disable a layout update 
44239      */
44240     endUpdate : function(noLayout){
44241         this.updating = false;
44242         if(!noLayout){
44243             this.layout();
44244         }    
44245     },
44246     
44247     layout: function(){
44248         
44249     },
44250     
44251     onRegionResized : function(region, newSize){
44252         this.fireEvent("regionresized", region, newSize);
44253         this.layout();
44254     },
44255     
44256     onRegionCollapsed : function(region){
44257         this.fireEvent("regioncollapsed", region);
44258     },
44259     
44260     onRegionExpanded : function(region){
44261         this.fireEvent("regionexpanded", region);
44262     },
44263         
44264     /**
44265      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44266      * performs box-model adjustments.
44267      * @return {Object} The size as an object {width: (the width), height: (the height)}
44268      */
44269     getViewSize : function(){
44270         var size;
44271         if(this.el.dom != document.body){
44272             size = this.el.getSize();
44273         }else{
44274             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44275         }
44276         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44277         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44278         return size;
44279     },
44280     
44281     /**
44282      * Returns the Element this layout is bound to.
44283      * @return {Roo.Element}
44284      */
44285     getEl : function(){
44286         return this.el;
44287     },
44288     
44289     /**
44290      * Returns the specified region.
44291      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44292      * @return {Roo.LayoutRegion}
44293      */
44294     getRegion : function(target){
44295         return this.regions[target.toLowerCase()];
44296     },
44297     
44298     onWindowResize : function(){
44299         if(this.monitorWindowResize){
44300             this.layout();
44301         }
44302     }
44303 });/*
44304  * Based on:
44305  * Ext JS Library 1.1.1
44306  * Copyright(c) 2006-2007, Ext JS, LLC.
44307  *
44308  * Originally Released Under LGPL - original licence link has changed is not relivant.
44309  *
44310  * Fork - LGPL
44311  * <script type="text/javascript">
44312  */
44313 /**
44314  * @class Roo.BorderLayout
44315  * @extends Roo.LayoutManager
44316  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44317  * please see: <br><br>
44318  * <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>
44319  * <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>
44320  * Example:
44321  <pre><code>
44322  var layout = new Roo.BorderLayout(document.body, {
44323     north: {
44324         initialSize: 25,
44325         titlebar: false
44326     },
44327     west: {
44328         split:true,
44329         initialSize: 200,
44330         minSize: 175,
44331         maxSize: 400,
44332         titlebar: true,
44333         collapsible: true
44334     },
44335     east: {
44336         split:true,
44337         initialSize: 202,
44338         minSize: 175,
44339         maxSize: 400,
44340         titlebar: true,
44341         collapsible: true
44342     },
44343     south: {
44344         split:true,
44345         initialSize: 100,
44346         minSize: 100,
44347         maxSize: 200,
44348         titlebar: true,
44349         collapsible: true
44350     },
44351     center: {
44352         titlebar: true,
44353         autoScroll:true,
44354         resizeTabs: true,
44355         minTabWidth: 50,
44356         preferredTabWidth: 150
44357     }
44358 });
44359
44360 // shorthand
44361 var CP = Roo.ContentPanel;
44362
44363 layout.beginUpdate();
44364 layout.add("north", new CP("north", "North"));
44365 layout.add("south", new CP("south", {title: "South", closable: true}));
44366 layout.add("west", new CP("west", {title: "West"}));
44367 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44368 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44369 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44370 layout.getRegion("center").showPanel("center1");
44371 layout.endUpdate();
44372 </code></pre>
44373
44374 <b>The container the layout is rendered into can be either the body element or any other element.
44375 If it is not the body element, the container needs to either be an absolute positioned element,
44376 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44377 the container size if it is not the body element.</b>
44378
44379 * @constructor
44380 * Create a new BorderLayout
44381 * @param {String/HTMLElement/Element} container The container this layout is bound to
44382 * @param {Object} config Configuration options
44383  */
44384 Roo.BorderLayout = function(container, config){
44385     config = config || {};
44386     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44387     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44388     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44389         var target = this.factory.validRegions[i];
44390         if(config[target]){
44391             this.addRegion(target, config[target]);
44392         }
44393     }
44394 };
44395
44396 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44397     /**
44398      * Creates and adds a new region if it doesn't already exist.
44399      * @param {String} target The target region key (north, south, east, west or center).
44400      * @param {Object} config The regions config object
44401      * @return {BorderLayoutRegion} The new region
44402      */
44403     addRegion : function(target, config){
44404         if(!this.regions[target]){
44405             var r = this.factory.create(target, this, config);
44406             this.bindRegion(target, r);
44407         }
44408         return this.regions[target];
44409     },
44410
44411     // private (kinda)
44412     bindRegion : function(name, r){
44413         this.regions[name] = r;
44414         r.on("visibilitychange", this.layout, this);
44415         r.on("paneladded", this.layout, this);
44416         r.on("panelremoved", this.layout, this);
44417         r.on("invalidated", this.layout, this);
44418         r.on("resized", this.onRegionResized, this);
44419         r.on("collapsed", this.onRegionCollapsed, this);
44420         r.on("expanded", this.onRegionExpanded, this);
44421     },
44422
44423     /**
44424      * Performs a layout update.
44425      */
44426     layout : function(){
44427         if(this.updating) return;
44428         var size = this.getViewSize();
44429         var w = size.width;
44430         var h = size.height;
44431         var centerW = w;
44432         var centerH = h;
44433         var centerY = 0;
44434         var centerX = 0;
44435         //var x = 0, y = 0;
44436
44437         var rs = this.regions;
44438         var north = rs["north"];
44439         var south = rs["south"]; 
44440         var west = rs["west"];
44441         var east = rs["east"];
44442         var center = rs["center"];
44443         //if(this.hideOnLayout){ // not supported anymore
44444             //c.el.setStyle("display", "none");
44445         //}
44446         if(north && north.isVisible()){
44447             var b = north.getBox();
44448             var m = north.getMargins();
44449             b.width = w - (m.left+m.right);
44450             b.x = m.left;
44451             b.y = m.top;
44452             centerY = b.height + b.y + m.bottom;
44453             centerH -= centerY;
44454             north.updateBox(this.safeBox(b));
44455         }
44456         if(south && south.isVisible()){
44457             var b = south.getBox();
44458             var m = south.getMargins();
44459             b.width = w - (m.left+m.right);
44460             b.x = m.left;
44461             var totalHeight = (b.height + m.top + m.bottom);
44462             b.y = h - totalHeight + m.top;
44463             centerH -= totalHeight;
44464             south.updateBox(this.safeBox(b));
44465         }
44466         if(west && west.isVisible()){
44467             var b = west.getBox();
44468             var m = west.getMargins();
44469             b.height = centerH - (m.top+m.bottom);
44470             b.x = m.left;
44471             b.y = centerY + m.top;
44472             var totalWidth = (b.width + m.left + m.right);
44473             centerX += totalWidth;
44474             centerW -= totalWidth;
44475             west.updateBox(this.safeBox(b));
44476         }
44477         if(east && east.isVisible()){
44478             var b = east.getBox();
44479             var m = east.getMargins();
44480             b.height = centerH - (m.top+m.bottom);
44481             var totalWidth = (b.width + m.left + m.right);
44482             b.x = w - totalWidth + m.left;
44483             b.y = centerY + m.top;
44484             centerW -= totalWidth;
44485             east.updateBox(this.safeBox(b));
44486         }
44487         if(center){
44488             var m = center.getMargins();
44489             var centerBox = {
44490                 x: centerX + m.left,
44491                 y: centerY + m.top,
44492                 width: centerW - (m.left+m.right),
44493                 height: centerH - (m.top+m.bottom)
44494             };
44495             //if(this.hideOnLayout){
44496                 //center.el.setStyle("display", "block");
44497             //}
44498             center.updateBox(this.safeBox(centerBox));
44499         }
44500         this.el.repaint();
44501         this.fireEvent("layout", this);
44502     },
44503
44504     // private
44505     safeBox : function(box){
44506         box.width = Math.max(0, box.width);
44507         box.height = Math.max(0, box.height);
44508         return box;
44509     },
44510
44511     /**
44512      * Adds a ContentPanel (or subclass) to this layout.
44513      * @param {String} target The target region key (north, south, east, west or center).
44514      * @param {Roo.ContentPanel} panel The panel to add
44515      * @return {Roo.ContentPanel} The added panel
44516      */
44517     add : function(target, panel){
44518          
44519         target = target.toLowerCase();
44520         return this.regions[target].add(panel);
44521     },
44522
44523     /**
44524      * Remove a ContentPanel (or subclass) to this layout.
44525      * @param {String} target The target region key (north, south, east, west or center).
44526      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44527      * @return {Roo.ContentPanel} The removed panel
44528      */
44529     remove : function(target, panel){
44530         target = target.toLowerCase();
44531         return this.regions[target].remove(panel);
44532     },
44533
44534     /**
44535      * Searches all regions for a panel with the specified id
44536      * @param {String} panelId
44537      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44538      */
44539     findPanel : function(panelId){
44540         var rs = this.regions;
44541         for(var target in rs){
44542             if(typeof rs[target] != "function"){
44543                 var p = rs[target].getPanel(panelId);
44544                 if(p){
44545                     return p;
44546                 }
44547             }
44548         }
44549         return null;
44550     },
44551
44552     /**
44553      * Searches all regions for a panel with the specified id and activates (shows) it.
44554      * @param {String/ContentPanel} panelId The panels id or the panel itself
44555      * @return {Roo.ContentPanel} The shown panel or null
44556      */
44557     showPanel : function(panelId) {
44558       var rs = this.regions;
44559       for(var target in rs){
44560          var r = rs[target];
44561          if(typeof r != "function"){
44562             if(r.hasPanel(panelId)){
44563                return r.showPanel(panelId);
44564             }
44565          }
44566       }
44567       return null;
44568    },
44569
44570    /**
44571      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44572      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44573      */
44574     restoreState : function(provider){
44575         if(!provider){
44576             provider = Roo.state.Manager;
44577         }
44578         var sm = new Roo.LayoutStateManager();
44579         sm.init(this, provider);
44580     },
44581
44582     /**
44583      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44584      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44585      * a valid ContentPanel config object.  Example:
44586      * <pre><code>
44587 // Create the main layout
44588 var layout = new Roo.BorderLayout('main-ct', {
44589     west: {
44590         split:true,
44591         minSize: 175,
44592         titlebar: true
44593     },
44594     center: {
44595         title:'Components'
44596     }
44597 }, 'main-ct');
44598
44599 // Create and add multiple ContentPanels at once via configs
44600 layout.batchAdd({
44601    west: {
44602        id: 'source-files',
44603        autoCreate:true,
44604        title:'Ext Source Files',
44605        autoScroll:true,
44606        fitToFrame:true
44607    },
44608    center : {
44609        el: cview,
44610        autoScroll:true,
44611        fitToFrame:true,
44612        toolbar: tb,
44613        resizeEl:'cbody'
44614    }
44615 });
44616 </code></pre>
44617      * @param {Object} regions An object containing ContentPanel configs by region name
44618      */
44619     batchAdd : function(regions){
44620         this.beginUpdate();
44621         for(var rname in regions){
44622             var lr = this.regions[rname];
44623             if(lr){
44624                 this.addTypedPanels(lr, regions[rname]);
44625             }
44626         }
44627         this.endUpdate();
44628     },
44629
44630     // private
44631     addTypedPanels : function(lr, ps){
44632         if(typeof ps == 'string'){
44633             lr.add(new Roo.ContentPanel(ps));
44634         }
44635         else if(ps instanceof Array){
44636             for(var i =0, len = ps.length; i < len; i++){
44637                 this.addTypedPanels(lr, ps[i]);
44638             }
44639         }
44640         else if(!ps.events){ // raw config?
44641             var el = ps.el;
44642             delete ps.el; // prevent conflict
44643             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44644         }
44645         else {  // panel object assumed!
44646             lr.add(ps);
44647         }
44648     },
44649     /**
44650      * Adds a xtype elements to the layout.
44651      * <pre><code>
44652
44653 layout.addxtype({
44654        xtype : 'ContentPanel',
44655        region: 'west',
44656        items: [ .... ]
44657    }
44658 );
44659
44660 layout.addxtype({
44661         xtype : 'NestedLayoutPanel',
44662         region: 'west',
44663         layout: {
44664            center: { },
44665            west: { }   
44666         },
44667         items : [ ... list of content panels or nested layout panels.. ]
44668    }
44669 );
44670 </code></pre>
44671      * @param {Object} cfg Xtype definition of item to add.
44672      */
44673     addxtype : function(cfg)
44674     {
44675         // basically accepts a pannel...
44676         // can accept a layout region..!?!?
44677         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44678         
44679         if (!cfg.xtype.match(/Panel$/)) {
44680             return false;
44681         }
44682         var ret = false;
44683         
44684         if (typeof(cfg.region) == 'undefined') {
44685             Roo.log("Failed to add Panel, region was not set");
44686             Roo.log(cfg);
44687             return false;
44688         }
44689         var region = cfg.region;
44690         delete cfg.region;
44691         
44692           
44693         var xitems = [];
44694         if (cfg.items) {
44695             xitems = cfg.items;
44696             delete cfg.items;
44697         }
44698         
44699         
44700         switch(cfg.xtype) 
44701         {
44702             case 'ContentPanel':  // ContentPanel (el, cfg)
44703             case 'ScrollPanel':  // ContentPanel (el, cfg)
44704                 if(cfg.autoCreate) {
44705                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44706                 } else {
44707                     var el = this.el.createChild();
44708                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44709                 }
44710                 
44711                 this.add(region, ret);
44712                 break;
44713             
44714             
44715             case 'TreePanel': // our new panel!
44716                 cfg.el = this.el.createChild();
44717                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44718                 this.add(region, ret);
44719                 break;
44720             
44721             case 'NestedLayoutPanel': 
44722                 // create a new Layout (which is  a Border Layout...
44723                 var el = this.el.createChild();
44724                 var clayout = cfg.layout;
44725                 delete cfg.layout;
44726                 clayout.items   = clayout.items  || [];
44727                 // replace this exitems with the clayout ones..
44728                 xitems = clayout.items;
44729                  
44730                 
44731                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44732                     cfg.background = false;
44733                 }
44734                 var layout = new Roo.BorderLayout(el, clayout);
44735                 
44736                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44737                 //console.log('adding nested layout panel '  + cfg.toSource());
44738                 this.add(region, ret);
44739                 
44740                 break;
44741                 
44742             case 'GridPanel': 
44743             
44744                 // needs grid and region
44745                 
44746                 //var el = this.getRegion(region).el.createChild();
44747                 var el = this.el.createChild();
44748                 // create the grid first...
44749                 
44750                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44751                 delete cfg.grid;
44752                 if (region == 'center' && this.active ) {
44753                     cfg.background = false;
44754                 }
44755                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44756                 
44757                 this.add(region, ret);
44758                 if (cfg.background) {
44759                     ret.on('activate', function(gp) {
44760                         if (!gp.grid.rendered) {
44761                             gp.grid.render();
44762                         }
44763                     });
44764                 } else {
44765                     grid.render();
44766                 }
44767                 break;
44768            
44769                
44770                 
44771                 
44772             default: 
44773                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44774                 return null;
44775              // GridPanel (grid, cfg)
44776             
44777         }
44778         this.beginUpdate();
44779         // add children..
44780         Roo.each(xitems, function(i)  {
44781             ret.addxtype(i);
44782         });
44783         this.endUpdate();
44784         return ret;
44785         
44786     }
44787 });
44788
44789 /**
44790  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44791  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44792  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44793  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44794  * <pre><code>
44795 // shorthand
44796 var CP = Roo.ContentPanel;
44797
44798 var layout = Roo.BorderLayout.create({
44799     north: {
44800         initialSize: 25,
44801         titlebar: false,
44802         panels: [new CP("north", "North")]
44803     },
44804     west: {
44805         split:true,
44806         initialSize: 200,
44807         minSize: 175,
44808         maxSize: 400,
44809         titlebar: true,
44810         collapsible: true,
44811         panels: [new CP("west", {title: "West"})]
44812     },
44813     east: {
44814         split:true,
44815         initialSize: 202,
44816         minSize: 175,
44817         maxSize: 400,
44818         titlebar: true,
44819         collapsible: true,
44820         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44821     },
44822     south: {
44823         split:true,
44824         initialSize: 100,
44825         minSize: 100,
44826         maxSize: 200,
44827         titlebar: true,
44828         collapsible: true,
44829         panels: [new CP("south", {title: "South", closable: true})]
44830     },
44831     center: {
44832         titlebar: true,
44833         autoScroll:true,
44834         resizeTabs: true,
44835         minTabWidth: 50,
44836         preferredTabWidth: 150,
44837         panels: [
44838             new CP("center1", {title: "Close Me", closable: true}),
44839             new CP("center2", {title: "Center Panel", closable: false})
44840         ]
44841     }
44842 }, document.body);
44843
44844 layout.getRegion("center").showPanel("center1");
44845 </code></pre>
44846  * @param config
44847  * @param targetEl
44848  */
44849 Roo.BorderLayout.create = function(config, targetEl){
44850     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44851     layout.beginUpdate();
44852     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44853     for(var j = 0, jlen = regions.length; j < jlen; j++){
44854         var lr = regions[j];
44855         if(layout.regions[lr] && config[lr].panels){
44856             var r = layout.regions[lr];
44857             var ps = config[lr].panels;
44858             layout.addTypedPanels(r, ps);
44859         }
44860     }
44861     layout.endUpdate();
44862     return layout;
44863 };
44864
44865 // private
44866 Roo.BorderLayout.RegionFactory = {
44867     // private
44868     validRegions : ["north","south","east","west","center"],
44869
44870     // private
44871     create : function(target, mgr, config){
44872         target = target.toLowerCase();
44873         if(config.lightweight || config.basic){
44874             return new Roo.BasicLayoutRegion(mgr, config, target);
44875         }
44876         switch(target){
44877             case "north":
44878                 return new Roo.NorthLayoutRegion(mgr, config);
44879             case "south":
44880                 return new Roo.SouthLayoutRegion(mgr, config);
44881             case "east":
44882                 return new Roo.EastLayoutRegion(mgr, config);
44883             case "west":
44884                 return new Roo.WestLayoutRegion(mgr, config);
44885             case "center":
44886                 return new Roo.CenterLayoutRegion(mgr, config);
44887         }
44888         throw 'Layout region "'+target+'" not supported.';
44889     }
44890 };/*
44891  * Based on:
44892  * Ext JS Library 1.1.1
44893  * Copyright(c) 2006-2007, Ext JS, LLC.
44894  *
44895  * Originally Released Under LGPL - original licence link has changed is not relivant.
44896  *
44897  * Fork - LGPL
44898  * <script type="text/javascript">
44899  */
44900  
44901 /**
44902  * @class Roo.BasicLayoutRegion
44903  * @extends Roo.util.Observable
44904  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44905  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44906  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44907  */
44908 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44909     this.mgr = mgr;
44910     this.position  = pos;
44911     this.events = {
44912         /**
44913          * @scope Roo.BasicLayoutRegion
44914          */
44915         
44916         /**
44917          * @event beforeremove
44918          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44919          * @param {Roo.LayoutRegion} this
44920          * @param {Roo.ContentPanel} panel The panel
44921          * @param {Object} e The cancel event object
44922          */
44923         "beforeremove" : true,
44924         /**
44925          * @event invalidated
44926          * Fires when the layout for this region is changed.
44927          * @param {Roo.LayoutRegion} this
44928          */
44929         "invalidated" : true,
44930         /**
44931          * @event visibilitychange
44932          * Fires when this region is shown or hidden 
44933          * @param {Roo.LayoutRegion} this
44934          * @param {Boolean} visibility true or false
44935          */
44936         "visibilitychange" : true,
44937         /**
44938          * @event paneladded
44939          * Fires when a panel is added. 
44940          * @param {Roo.LayoutRegion} this
44941          * @param {Roo.ContentPanel} panel The panel
44942          */
44943         "paneladded" : true,
44944         /**
44945          * @event panelremoved
44946          * Fires when a panel is removed. 
44947          * @param {Roo.LayoutRegion} this
44948          * @param {Roo.ContentPanel} panel The panel
44949          */
44950         "panelremoved" : true,
44951         /**
44952          * @event collapsed
44953          * Fires when this region is collapsed.
44954          * @param {Roo.LayoutRegion} this
44955          */
44956         "collapsed" : true,
44957         /**
44958          * @event expanded
44959          * Fires when this region is expanded.
44960          * @param {Roo.LayoutRegion} this
44961          */
44962         "expanded" : true,
44963         /**
44964          * @event slideshow
44965          * Fires when this region is slid into view.
44966          * @param {Roo.LayoutRegion} this
44967          */
44968         "slideshow" : true,
44969         /**
44970          * @event slidehide
44971          * Fires when this region slides out of view. 
44972          * @param {Roo.LayoutRegion} this
44973          */
44974         "slidehide" : true,
44975         /**
44976          * @event panelactivated
44977          * Fires when a panel is activated. 
44978          * @param {Roo.LayoutRegion} this
44979          * @param {Roo.ContentPanel} panel The activated panel
44980          */
44981         "panelactivated" : true,
44982         /**
44983          * @event resized
44984          * Fires when the user resizes this region. 
44985          * @param {Roo.LayoutRegion} this
44986          * @param {Number} newSize The new size (width for east/west, height for north/south)
44987          */
44988         "resized" : true
44989     };
44990     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44991     this.panels = new Roo.util.MixedCollection();
44992     this.panels.getKey = this.getPanelId.createDelegate(this);
44993     this.box = null;
44994     this.activePanel = null;
44995     // ensure listeners are added...
44996     
44997     if (config.listeners || config.events) {
44998         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44999             listeners : config.listeners || {},
45000             events : config.events || {}
45001         });
45002     }
45003     
45004     if(skipConfig !== true){
45005         this.applyConfig(config);
45006     }
45007 };
45008
45009 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45010     getPanelId : function(p){
45011         return p.getId();
45012     },
45013     
45014     applyConfig : function(config){
45015         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45016         this.config = config;
45017         
45018     },
45019     
45020     /**
45021      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45022      * the width, for horizontal (north, south) the height.
45023      * @param {Number} newSize The new width or height
45024      */
45025     resizeTo : function(newSize){
45026         var el = this.el ? this.el :
45027                  (this.activePanel ? this.activePanel.getEl() : null);
45028         if(el){
45029             switch(this.position){
45030                 case "east":
45031                 case "west":
45032                     el.setWidth(newSize);
45033                     this.fireEvent("resized", this, newSize);
45034                 break;
45035                 case "north":
45036                 case "south":
45037                     el.setHeight(newSize);
45038                     this.fireEvent("resized", this, newSize);
45039                 break;                
45040             }
45041         }
45042     },
45043     
45044     getBox : function(){
45045         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45046     },
45047     
45048     getMargins : function(){
45049         return this.margins;
45050     },
45051     
45052     updateBox : function(box){
45053         this.box = box;
45054         var el = this.activePanel.getEl();
45055         el.dom.style.left = box.x + "px";
45056         el.dom.style.top = box.y + "px";
45057         this.activePanel.setSize(box.width, box.height);
45058     },
45059     
45060     /**
45061      * Returns the container element for this region.
45062      * @return {Roo.Element}
45063      */
45064     getEl : function(){
45065         return this.activePanel;
45066     },
45067     
45068     /**
45069      * Returns true if this region is currently visible.
45070      * @return {Boolean}
45071      */
45072     isVisible : function(){
45073         return this.activePanel ? true : false;
45074     },
45075     
45076     setActivePanel : function(panel){
45077         panel = this.getPanel(panel);
45078         if(this.activePanel && this.activePanel != panel){
45079             this.activePanel.setActiveState(false);
45080             this.activePanel.getEl().setLeftTop(-10000,-10000);
45081         }
45082         this.activePanel = panel;
45083         panel.setActiveState(true);
45084         if(this.box){
45085             panel.setSize(this.box.width, this.box.height);
45086         }
45087         this.fireEvent("panelactivated", this, panel);
45088         this.fireEvent("invalidated");
45089     },
45090     
45091     /**
45092      * Show the specified panel.
45093      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45094      * @return {Roo.ContentPanel} The shown panel or null
45095      */
45096     showPanel : function(panel){
45097         if(panel = this.getPanel(panel)){
45098             this.setActivePanel(panel);
45099         }
45100         return panel;
45101     },
45102     
45103     /**
45104      * Get the active panel for this region.
45105      * @return {Roo.ContentPanel} The active panel or null
45106      */
45107     getActivePanel : function(){
45108         return this.activePanel;
45109     },
45110     
45111     /**
45112      * Add the passed ContentPanel(s)
45113      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45114      * @return {Roo.ContentPanel} The panel added (if only one was added)
45115      */
45116     add : function(panel){
45117         if(arguments.length > 1){
45118             for(var i = 0, len = arguments.length; i < len; i++) {
45119                 this.add(arguments[i]);
45120             }
45121             return null;
45122         }
45123         if(this.hasPanel(panel)){
45124             this.showPanel(panel);
45125             return panel;
45126         }
45127         var el = panel.getEl();
45128         if(el.dom.parentNode != this.mgr.el.dom){
45129             this.mgr.el.dom.appendChild(el.dom);
45130         }
45131         if(panel.setRegion){
45132             panel.setRegion(this);
45133         }
45134         this.panels.add(panel);
45135         el.setStyle("position", "absolute");
45136         if(!panel.background){
45137             this.setActivePanel(panel);
45138             if(this.config.initialSize && this.panels.getCount()==1){
45139                 this.resizeTo(this.config.initialSize);
45140             }
45141         }
45142         this.fireEvent("paneladded", this, panel);
45143         return panel;
45144     },
45145     
45146     /**
45147      * Returns true if the panel is in this region.
45148      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45149      * @return {Boolean}
45150      */
45151     hasPanel : function(panel){
45152         if(typeof panel == "object"){ // must be panel obj
45153             panel = panel.getId();
45154         }
45155         return this.getPanel(panel) ? true : false;
45156     },
45157     
45158     /**
45159      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45160      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45161      * @param {Boolean} preservePanel Overrides the config preservePanel option
45162      * @return {Roo.ContentPanel} The panel that was removed
45163      */
45164     remove : function(panel, preservePanel){
45165         panel = this.getPanel(panel);
45166         if(!panel){
45167             return null;
45168         }
45169         var e = {};
45170         this.fireEvent("beforeremove", this, panel, e);
45171         if(e.cancel === true){
45172             return null;
45173         }
45174         var panelId = panel.getId();
45175         this.panels.removeKey(panelId);
45176         return panel;
45177     },
45178     
45179     /**
45180      * Returns the panel specified or null if it's not in this region.
45181      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45182      * @return {Roo.ContentPanel}
45183      */
45184     getPanel : function(id){
45185         if(typeof id == "object"){ // must be panel obj
45186             return id;
45187         }
45188         return this.panels.get(id);
45189     },
45190     
45191     /**
45192      * Returns this regions position (north/south/east/west/center).
45193      * @return {String} 
45194      */
45195     getPosition: function(){
45196         return this.position;    
45197     }
45198 });/*
45199  * Based on:
45200  * Ext JS Library 1.1.1
45201  * Copyright(c) 2006-2007, Ext JS, LLC.
45202  *
45203  * Originally Released Under LGPL - original licence link has changed is not relivant.
45204  *
45205  * Fork - LGPL
45206  * <script type="text/javascript">
45207  */
45208  
45209 /**
45210  * @class Roo.LayoutRegion
45211  * @extends Roo.BasicLayoutRegion
45212  * This class represents a region in a layout manager.
45213  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45214  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45215  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45216  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45217  * @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})
45218  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45219  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45220  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45221  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45222  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45223  * @cfg {String}    title           The title for the region (overrides panel titles)
45224  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45225  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45226  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45227  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45228  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45229  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45230  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45231  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45232  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45233  * @cfg {Boolean}   showPin         True to show a pin button
45234  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45235  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45236  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45237  * @cfg {Number}    width           For East/West panels
45238  * @cfg {Number}    height          For North/South panels
45239  * @cfg {Boolean}   split           To show the splitter
45240  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45241  */
45242 Roo.LayoutRegion = function(mgr, config, pos){
45243     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45244     var dh = Roo.DomHelper;
45245     /** This region's container element 
45246     * @type Roo.Element */
45247     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45248     /** This region's title element 
45249     * @type Roo.Element */
45250
45251     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45252         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45253         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45254     ]}, true);
45255     this.titleEl.enableDisplayMode();
45256     /** This region's title text element 
45257     * @type HTMLElement */
45258     this.titleTextEl = this.titleEl.dom.firstChild;
45259     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45260     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45261     this.closeBtn.enableDisplayMode();
45262     this.closeBtn.on("click", this.closeClicked, this);
45263     this.closeBtn.hide();
45264
45265     this.createBody(config);
45266     this.visible = true;
45267     this.collapsed = false;
45268
45269     if(config.hideWhenEmpty){
45270         this.hide();
45271         this.on("paneladded", this.validateVisibility, this);
45272         this.on("panelremoved", this.validateVisibility, this);
45273     }
45274     this.applyConfig(config);
45275 };
45276
45277 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45278
45279     createBody : function(){
45280         /** This region's body element 
45281         * @type Roo.Element */
45282         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45283     },
45284
45285     applyConfig : function(c){
45286         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45287             var dh = Roo.DomHelper;
45288             if(c.titlebar !== false){
45289                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45290                 this.collapseBtn.on("click", this.collapse, this);
45291                 this.collapseBtn.enableDisplayMode();
45292
45293                 if(c.showPin === true || this.showPin){
45294                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45295                     this.stickBtn.enableDisplayMode();
45296                     this.stickBtn.on("click", this.expand, this);
45297                     this.stickBtn.hide();
45298                 }
45299             }
45300             /** This region's collapsed element
45301             * @type Roo.Element */
45302             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45303                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45304             ]}, true);
45305             if(c.floatable !== false){
45306                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45307                this.collapsedEl.on("click", this.collapseClick, this);
45308             }
45309
45310             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45311                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45312                    id: "message", unselectable: "on", style:{"float":"left"}});
45313                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45314              }
45315             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45316             this.expandBtn.on("click", this.expand, this);
45317         }
45318         if(this.collapseBtn){
45319             this.collapseBtn.setVisible(c.collapsible == true);
45320         }
45321         this.cmargins = c.cmargins || this.cmargins ||
45322                          (this.position == "west" || this.position == "east" ?
45323                              {top: 0, left: 2, right:2, bottom: 0} :
45324                              {top: 2, left: 0, right:0, bottom: 2});
45325         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45326         this.bottomTabs = c.tabPosition != "top";
45327         this.autoScroll = c.autoScroll || false;
45328         if(this.autoScroll){
45329             this.bodyEl.setStyle("overflow", "auto");
45330         }else{
45331             this.bodyEl.setStyle("overflow", "hidden");
45332         }
45333         //if(c.titlebar !== false){
45334             if((!c.titlebar && !c.title) || c.titlebar === false){
45335                 this.titleEl.hide();
45336             }else{
45337                 this.titleEl.show();
45338                 if(c.title){
45339                     this.titleTextEl.innerHTML = c.title;
45340                 }
45341             }
45342         //}
45343         this.duration = c.duration || .30;
45344         this.slideDuration = c.slideDuration || .45;
45345         this.config = c;
45346         if(c.collapsed){
45347             this.collapse(true);
45348         }
45349         if(c.hidden){
45350             this.hide();
45351         }
45352     },
45353     /**
45354      * Returns true if this region is currently visible.
45355      * @return {Boolean}
45356      */
45357     isVisible : function(){
45358         return this.visible;
45359     },
45360
45361     /**
45362      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45363      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45364      */
45365     setCollapsedTitle : function(title){
45366         title = title || "&#160;";
45367         if(this.collapsedTitleTextEl){
45368             this.collapsedTitleTextEl.innerHTML = title;
45369         }
45370     },
45371
45372     getBox : function(){
45373         var b;
45374         if(!this.collapsed){
45375             b = this.el.getBox(false, true);
45376         }else{
45377             b = this.collapsedEl.getBox(false, true);
45378         }
45379         return b;
45380     },
45381
45382     getMargins : function(){
45383         return this.collapsed ? this.cmargins : this.margins;
45384     },
45385
45386     highlight : function(){
45387         this.el.addClass("x-layout-panel-dragover");
45388     },
45389
45390     unhighlight : function(){
45391         this.el.removeClass("x-layout-panel-dragover");
45392     },
45393
45394     updateBox : function(box){
45395         this.box = box;
45396         if(!this.collapsed){
45397             this.el.dom.style.left = box.x + "px";
45398             this.el.dom.style.top = box.y + "px";
45399             this.updateBody(box.width, box.height);
45400         }else{
45401             this.collapsedEl.dom.style.left = box.x + "px";
45402             this.collapsedEl.dom.style.top = box.y + "px";
45403             this.collapsedEl.setSize(box.width, box.height);
45404         }
45405         if(this.tabs){
45406             this.tabs.autoSizeTabs();
45407         }
45408     },
45409
45410     updateBody : function(w, h){
45411         if(w !== null){
45412             this.el.setWidth(w);
45413             w -= this.el.getBorderWidth("rl");
45414             if(this.config.adjustments){
45415                 w += this.config.adjustments[0];
45416             }
45417         }
45418         if(h !== null){
45419             this.el.setHeight(h);
45420             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45421             h -= this.el.getBorderWidth("tb");
45422             if(this.config.adjustments){
45423                 h += this.config.adjustments[1];
45424             }
45425             this.bodyEl.setHeight(h);
45426             if(this.tabs){
45427                 h = this.tabs.syncHeight(h);
45428             }
45429         }
45430         if(this.panelSize){
45431             w = w !== null ? w : this.panelSize.width;
45432             h = h !== null ? h : this.panelSize.height;
45433         }
45434         if(this.activePanel){
45435             var el = this.activePanel.getEl();
45436             w = w !== null ? w : el.getWidth();
45437             h = h !== null ? h : el.getHeight();
45438             this.panelSize = {width: w, height: h};
45439             this.activePanel.setSize(w, h);
45440         }
45441         if(Roo.isIE && this.tabs){
45442             this.tabs.el.repaint();
45443         }
45444     },
45445
45446     /**
45447      * Returns the container element for this region.
45448      * @return {Roo.Element}
45449      */
45450     getEl : function(){
45451         return this.el;
45452     },
45453
45454     /**
45455      * Hides this region.
45456      */
45457     hide : function(){
45458         if(!this.collapsed){
45459             this.el.dom.style.left = "-2000px";
45460             this.el.hide();
45461         }else{
45462             this.collapsedEl.dom.style.left = "-2000px";
45463             this.collapsedEl.hide();
45464         }
45465         this.visible = false;
45466         this.fireEvent("visibilitychange", this, false);
45467     },
45468
45469     /**
45470      * Shows this region if it was previously hidden.
45471      */
45472     show : function(){
45473         if(!this.collapsed){
45474             this.el.show();
45475         }else{
45476             this.collapsedEl.show();
45477         }
45478         this.visible = true;
45479         this.fireEvent("visibilitychange", this, true);
45480     },
45481
45482     closeClicked : function(){
45483         if(this.activePanel){
45484             this.remove(this.activePanel);
45485         }
45486     },
45487
45488     collapseClick : function(e){
45489         if(this.isSlid){
45490            e.stopPropagation();
45491            this.slideIn();
45492         }else{
45493            e.stopPropagation();
45494            this.slideOut();
45495         }
45496     },
45497
45498     /**
45499      * Collapses this region.
45500      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45501      */
45502     collapse : function(skipAnim){
45503         if(this.collapsed) return;
45504         this.collapsed = true;
45505         if(this.split){
45506             this.split.el.hide();
45507         }
45508         if(this.config.animate && skipAnim !== true){
45509             this.fireEvent("invalidated", this);
45510             this.animateCollapse();
45511         }else{
45512             this.el.setLocation(-20000,-20000);
45513             this.el.hide();
45514             this.collapsedEl.show();
45515             this.fireEvent("collapsed", this);
45516             this.fireEvent("invalidated", this);
45517         }
45518     },
45519
45520     animateCollapse : function(){
45521         // overridden
45522     },
45523
45524     /**
45525      * Expands this region if it was previously collapsed.
45526      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45527      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45528      */
45529     expand : function(e, skipAnim){
45530         if(e) e.stopPropagation();
45531         if(!this.collapsed || this.el.hasActiveFx()) return;
45532         if(this.isSlid){
45533             this.afterSlideIn();
45534             skipAnim = true;
45535         }
45536         this.collapsed = false;
45537         if(this.config.animate && skipAnim !== true){
45538             this.animateExpand();
45539         }else{
45540             this.el.show();
45541             if(this.split){
45542                 this.split.el.show();
45543             }
45544             this.collapsedEl.setLocation(-2000,-2000);
45545             this.collapsedEl.hide();
45546             this.fireEvent("invalidated", this);
45547             this.fireEvent("expanded", this);
45548         }
45549     },
45550
45551     animateExpand : function(){
45552         // overridden
45553     },
45554
45555     initTabs : function()
45556     {
45557         this.bodyEl.setStyle("overflow", "hidden");
45558         var ts = new Roo.TabPanel(
45559                 this.bodyEl.dom,
45560                 {
45561                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45562                     disableTooltips: this.config.disableTabTips,
45563                     toolbar : this.config.toolbar
45564                 }
45565         );
45566         if(this.config.hideTabs){
45567             ts.stripWrap.setDisplayed(false);
45568         }
45569         this.tabs = ts;
45570         ts.resizeTabs = this.config.resizeTabs === true;
45571         ts.minTabWidth = this.config.minTabWidth || 40;
45572         ts.maxTabWidth = this.config.maxTabWidth || 250;
45573         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45574         ts.monitorResize = false;
45575         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45576         ts.bodyEl.addClass('x-layout-tabs-body');
45577         this.panels.each(this.initPanelAsTab, this);
45578     },
45579
45580     initPanelAsTab : function(panel){
45581         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45582                     this.config.closeOnTab && panel.isClosable());
45583         if(panel.tabTip !== undefined){
45584             ti.setTooltip(panel.tabTip);
45585         }
45586         ti.on("activate", function(){
45587               this.setActivePanel(panel);
45588         }, this);
45589         if(this.config.closeOnTab){
45590             ti.on("beforeclose", function(t, e){
45591                 e.cancel = true;
45592                 this.remove(panel);
45593             }, this);
45594         }
45595         return ti;
45596     },
45597
45598     updatePanelTitle : function(panel, title){
45599         if(this.activePanel == panel){
45600             this.updateTitle(title);
45601         }
45602         if(this.tabs){
45603             var ti = this.tabs.getTab(panel.getEl().id);
45604             ti.setText(title);
45605             if(panel.tabTip !== undefined){
45606                 ti.setTooltip(panel.tabTip);
45607             }
45608         }
45609     },
45610
45611     updateTitle : function(title){
45612         if(this.titleTextEl && !this.config.title){
45613             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45614         }
45615     },
45616
45617     setActivePanel : function(panel){
45618         panel = this.getPanel(panel);
45619         if(this.activePanel && this.activePanel != panel){
45620             this.activePanel.setActiveState(false);
45621         }
45622         this.activePanel = panel;
45623         panel.setActiveState(true);
45624         if(this.panelSize){
45625             panel.setSize(this.panelSize.width, this.panelSize.height);
45626         }
45627         if(this.closeBtn){
45628             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45629         }
45630         this.updateTitle(panel.getTitle());
45631         if(this.tabs){
45632             this.fireEvent("invalidated", this);
45633         }
45634         this.fireEvent("panelactivated", this, panel);
45635     },
45636
45637     /**
45638      * Shows the specified panel.
45639      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45640      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45641      */
45642     showPanel : function(panel){
45643         if(panel = this.getPanel(panel)){
45644             if(this.tabs){
45645                 var tab = this.tabs.getTab(panel.getEl().id);
45646                 if(tab.isHidden()){
45647                     this.tabs.unhideTab(tab.id);
45648                 }
45649                 tab.activate();
45650             }else{
45651                 this.setActivePanel(panel);
45652             }
45653         }
45654         return panel;
45655     },
45656
45657     /**
45658      * Get the active panel for this region.
45659      * @return {Roo.ContentPanel} The active panel or null
45660      */
45661     getActivePanel : function(){
45662         return this.activePanel;
45663     },
45664
45665     validateVisibility : function(){
45666         if(this.panels.getCount() < 1){
45667             this.updateTitle("&#160;");
45668             this.closeBtn.hide();
45669             this.hide();
45670         }else{
45671             if(!this.isVisible()){
45672                 this.show();
45673             }
45674         }
45675     },
45676
45677     /**
45678      * Adds the passed ContentPanel(s) to this region.
45679      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45680      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45681      */
45682     add : function(panel){
45683         if(arguments.length > 1){
45684             for(var i = 0, len = arguments.length; i < len; i++) {
45685                 this.add(arguments[i]);
45686             }
45687             return null;
45688         }
45689         if(this.hasPanel(panel)){
45690             this.showPanel(panel);
45691             return panel;
45692         }
45693         panel.setRegion(this);
45694         this.panels.add(panel);
45695         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45696             this.bodyEl.dom.appendChild(panel.getEl().dom);
45697             if(panel.background !== true){
45698                 this.setActivePanel(panel);
45699             }
45700             this.fireEvent("paneladded", this, panel);
45701             return panel;
45702         }
45703         if(!this.tabs){
45704             this.initTabs();
45705         }else{
45706             this.initPanelAsTab(panel);
45707         }
45708         if(panel.background !== true){
45709             this.tabs.activate(panel.getEl().id);
45710         }
45711         this.fireEvent("paneladded", this, panel);
45712         return panel;
45713     },
45714
45715     /**
45716      * Hides the tab for the specified panel.
45717      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45718      */
45719     hidePanel : function(panel){
45720         if(this.tabs && (panel = this.getPanel(panel))){
45721             this.tabs.hideTab(panel.getEl().id);
45722         }
45723     },
45724
45725     /**
45726      * Unhides the tab for a previously hidden panel.
45727      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45728      */
45729     unhidePanel : function(panel){
45730         if(this.tabs && (panel = this.getPanel(panel))){
45731             this.tabs.unhideTab(panel.getEl().id);
45732         }
45733     },
45734
45735     clearPanels : function(){
45736         while(this.panels.getCount() > 0){
45737              this.remove(this.panels.first());
45738         }
45739     },
45740
45741     /**
45742      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45743      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45744      * @param {Boolean} preservePanel Overrides the config preservePanel option
45745      * @return {Roo.ContentPanel} The panel that was removed
45746      */
45747     remove : function(panel, preservePanel){
45748         panel = this.getPanel(panel);
45749         if(!panel){
45750             return null;
45751         }
45752         var e = {};
45753         this.fireEvent("beforeremove", this, panel, e);
45754         if(e.cancel === true){
45755             return null;
45756         }
45757         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45758         var panelId = panel.getId();
45759         this.panels.removeKey(panelId);
45760         if(preservePanel){
45761             document.body.appendChild(panel.getEl().dom);
45762         }
45763         if(this.tabs){
45764             this.tabs.removeTab(panel.getEl().id);
45765         }else if (!preservePanel){
45766             this.bodyEl.dom.removeChild(panel.getEl().dom);
45767         }
45768         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45769             var p = this.panels.first();
45770             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45771             tempEl.appendChild(p.getEl().dom);
45772             this.bodyEl.update("");
45773             this.bodyEl.dom.appendChild(p.getEl().dom);
45774             tempEl = null;
45775             this.updateTitle(p.getTitle());
45776             this.tabs = null;
45777             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45778             this.setActivePanel(p);
45779         }
45780         panel.setRegion(null);
45781         if(this.activePanel == panel){
45782             this.activePanel = null;
45783         }
45784         if(this.config.autoDestroy !== false && preservePanel !== true){
45785             try{panel.destroy();}catch(e){}
45786         }
45787         this.fireEvent("panelremoved", this, panel);
45788         return panel;
45789     },
45790
45791     /**
45792      * Returns the TabPanel component used by this region
45793      * @return {Roo.TabPanel}
45794      */
45795     getTabs : function(){
45796         return this.tabs;
45797     },
45798
45799     createTool : function(parentEl, className){
45800         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45801             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45802         btn.addClassOnOver("x-layout-tools-button-over");
45803         return btn;
45804     }
45805 });/*
45806  * Based on:
45807  * Ext JS Library 1.1.1
45808  * Copyright(c) 2006-2007, Ext JS, LLC.
45809  *
45810  * Originally Released Under LGPL - original licence link has changed is not relivant.
45811  *
45812  * Fork - LGPL
45813  * <script type="text/javascript">
45814  */
45815  
45816
45817
45818 /**
45819  * @class Roo.SplitLayoutRegion
45820  * @extends Roo.LayoutRegion
45821  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45822  */
45823 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45824     this.cursor = cursor;
45825     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45826 };
45827
45828 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45829     splitTip : "Drag to resize.",
45830     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45831     useSplitTips : false,
45832
45833     applyConfig : function(config){
45834         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45835         if(config.split){
45836             if(!this.split){
45837                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45838                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45839                 /** The SplitBar for this region 
45840                 * @type Roo.SplitBar */
45841                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45842                 this.split.on("moved", this.onSplitMove, this);
45843                 this.split.useShim = config.useShim === true;
45844                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45845                 if(this.useSplitTips){
45846                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45847                 }
45848                 if(config.collapsible){
45849                     this.split.el.on("dblclick", this.collapse,  this);
45850                 }
45851             }
45852             if(typeof config.minSize != "undefined"){
45853                 this.split.minSize = config.minSize;
45854             }
45855             if(typeof config.maxSize != "undefined"){
45856                 this.split.maxSize = config.maxSize;
45857             }
45858             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45859                 this.hideSplitter();
45860             }
45861         }
45862     },
45863
45864     getHMaxSize : function(){
45865          var cmax = this.config.maxSize || 10000;
45866          var center = this.mgr.getRegion("center");
45867          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45868     },
45869
45870     getVMaxSize : function(){
45871          var cmax = this.config.maxSize || 10000;
45872          var center = this.mgr.getRegion("center");
45873          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45874     },
45875
45876     onSplitMove : function(split, newSize){
45877         this.fireEvent("resized", this, newSize);
45878     },
45879     
45880     /** 
45881      * Returns the {@link Roo.SplitBar} for this region.
45882      * @return {Roo.SplitBar}
45883      */
45884     getSplitBar : function(){
45885         return this.split;
45886     },
45887     
45888     hide : function(){
45889         this.hideSplitter();
45890         Roo.SplitLayoutRegion.superclass.hide.call(this);
45891     },
45892
45893     hideSplitter : function(){
45894         if(this.split){
45895             this.split.el.setLocation(-2000,-2000);
45896             this.split.el.hide();
45897         }
45898     },
45899
45900     show : function(){
45901         if(this.split){
45902             this.split.el.show();
45903         }
45904         Roo.SplitLayoutRegion.superclass.show.call(this);
45905     },
45906     
45907     beforeSlide: function(){
45908         if(Roo.isGecko){// firefox overflow auto bug workaround
45909             this.bodyEl.clip();
45910             if(this.tabs) this.tabs.bodyEl.clip();
45911             if(this.activePanel){
45912                 this.activePanel.getEl().clip();
45913                 
45914                 if(this.activePanel.beforeSlide){
45915                     this.activePanel.beforeSlide();
45916                 }
45917             }
45918         }
45919     },
45920     
45921     afterSlide : function(){
45922         if(Roo.isGecko){// firefox overflow auto bug workaround
45923             this.bodyEl.unclip();
45924             if(this.tabs) this.tabs.bodyEl.unclip();
45925             if(this.activePanel){
45926                 this.activePanel.getEl().unclip();
45927                 if(this.activePanel.afterSlide){
45928                     this.activePanel.afterSlide();
45929                 }
45930             }
45931         }
45932     },
45933
45934     initAutoHide : function(){
45935         if(this.autoHide !== false){
45936             if(!this.autoHideHd){
45937                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45938                 this.autoHideHd = {
45939                     "mouseout": function(e){
45940                         if(!e.within(this.el, true)){
45941                             st.delay(500);
45942                         }
45943                     },
45944                     "mouseover" : function(e){
45945                         st.cancel();
45946                     },
45947                     scope : this
45948                 };
45949             }
45950             this.el.on(this.autoHideHd);
45951         }
45952     },
45953
45954     clearAutoHide : function(){
45955         if(this.autoHide !== false){
45956             this.el.un("mouseout", this.autoHideHd.mouseout);
45957             this.el.un("mouseover", this.autoHideHd.mouseover);
45958         }
45959     },
45960
45961     clearMonitor : function(){
45962         Roo.get(document).un("click", this.slideInIf, this);
45963     },
45964
45965     // these names are backwards but not changed for compat
45966     slideOut : function(){
45967         if(this.isSlid || this.el.hasActiveFx()){
45968             return;
45969         }
45970         this.isSlid = true;
45971         if(this.collapseBtn){
45972             this.collapseBtn.hide();
45973         }
45974         this.closeBtnState = this.closeBtn.getStyle('display');
45975         this.closeBtn.hide();
45976         if(this.stickBtn){
45977             this.stickBtn.show();
45978         }
45979         this.el.show();
45980         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45981         this.beforeSlide();
45982         this.el.setStyle("z-index", 10001);
45983         this.el.slideIn(this.getSlideAnchor(), {
45984             callback: function(){
45985                 this.afterSlide();
45986                 this.initAutoHide();
45987                 Roo.get(document).on("click", this.slideInIf, this);
45988                 this.fireEvent("slideshow", this);
45989             },
45990             scope: this,
45991             block: true
45992         });
45993     },
45994
45995     afterSlideIn : function(){
45996         this.clearAutoHide();
45997         this.isSlid = false;
45998         this.clearMonitor();
45999         this.el.setStyle("z-index", "");
46000         if(this.collapseBtn){
46001             this.collapseBtn.show();
46002         }
46003         this.closeBtn.setStyle('display', this.closeBtnState);
46004         if(this.stickBtn){
46005             this.stickBtn.hide();
46006         }
46007         this.fireEvent("slidehide", this);
46008     },
46009
46010     slideIn : function(cb){
46011         if(!this.isSlid || this.el.hasActiveFx()){
46012             Roo.callback(cb);
46013             return;
46014         }
46015         this.isSlid = false;
46016         this.beforeSlide();
46017         this.el.slideOut(this.getSlideAnchor(), {
46018             callback: function(){
46019                 this.el.setLeftTop(-10000, -10000);
46020                 this.afterSlide();
46021                 this.afterSlideIn();
46022                 Roo.callback(cb);
46023             },
46024             scope: this,
46025             block: true
46026         });
46027     },
46028     
46029     slideInIf : function(e){
46030         if(!e.within(this.el)){
46031             this.slideIn();
46032         }
46033     },
46034
46035     animateCollapse : function(){
46036         this.beforeSlide();
46037         this.el.setStyle("z-index", 20000);
46038         var anchor = this.getSlideAnchor();
46039         this.el.slideOut(anchor, {
46040             callback : function(){
46041                 this.el.setStyle("z-index", "");
46042                 this.collapsedEl.slideIn(anchor, {duration:.3});
46043                 this.afterSlide();
46044                 this.el.setLocation(-10000,-10000);
46045                 this.el.hide();
46046                 this.fireEvent("collapsed", this);
46047             },
46048             scope: this,
46049             block: true
46050         });
46051     },
46052
46053     animateExpand : function(){
46054         this.beforeSlide();
46055         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46056         this.el.setStyle("z-index", 20000);
46057         this.collapsedEl.hide({
46058             duration:.1
46059         });
46060         this.el.slideIn(this.getSlideAnchor(), {
46061             callback : function(){
46062                 this.el.setStyle("z-index", "");
46063                 this.afterSlide();
46064                 if(this.split){
46065                     this.split.el.show();
46066                 }
46067                 this.fireEvent("invalidated", this);
46068                 this.fireEvent("expanded", this);
46069             },
46070             scope: this,
46071             block: true
46072         });
46073     },
46074
46075     anchors : {
46076         "west" : "left",
46077         "east" : "right",
46078         "north" : "top",
46079         "south" : "bottom"
46080     },
46081
46082     sanchors : {
46083         "west" : "l",
46084         "east" : "r",
46085         "north" : "t",
46086         "south" : "b"
46087     },
46088
46089     canchors : {
46090         "west" : "tl-tr",
46091         "east" : "tr-tl",
46092         "north" : "tl-bl",
46093         "south" : "bl-tl"
46094     },
46095
46096     getAnchor : function(){
46097         return this.anchors[this.position];
46098     },
46099
46100     getCollapseAnchor : function(){
46101         return this.canchors[this.position];
46102     },
46103
46104     getSlideAnchor : function(){
46105         return this.sanchors[this.position];
46106     },
46107
46108     getAlignAdj : function(){
46109         var cm = this.cmargins;
46110         switch(this.position){
46111             case "west":
46112                 return [0, 0];
46113             break;
46114             case "east":
46115                 return [0, 0];
46116             break;
46117             case "north":
46118                 return [0, 0];
46119             break;
46120             case "south":
46121                 return [0, 0];
46122             break;
46123         }
46124     },
46125
46126     getExpandAdj : function(){
46127         var c = this.collapsedEl, cm = this.cmargins;
46128         switch(this.position){
46129             case "west":
46130                 return [-(cm.right+c.getWidth()+cm.left), 0];
46131             break;
46132             case "east":
46133                 return [cm.right+c.getWidth()+cm.left, 0];
46134             break;
46135             case "north":
46136                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46137             break;
46138             case "south":
46139                 return [0, cm.top+cm.bottom+c.getHeight()];
46140             break;
46141         }
46142     }
46143 });/*
46144  * Based on:
46145  * Ext JS Library 1.1.1
46146  * Copyright(c) 2006-2007, Ext JS, LLC.
46147  *
46148  * Originally Released Under LGPL - original licence link has changed is not relivant.
46149  *
46150  * Fork - LGPL
46151  * <script type="text/javascript">
46152  */
46153 /*
46154  * These classes are private internal classes
46155  */
46156 Roo.CenterLayoutRegion = function(mgr, config){
46157     Roo.LayoutRegion.call(this, mgr, config, "center");
46158     this.visible = true;
46159     this.minWidth = config.minWidth || 20;
46160     this.minHeight = config.minHeight || 20;
46161 };
46162
46163 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46164     hide : function(){
46165         // center panel can't be hidden
46166     },
46167     
46168     show : function(){
46169         // center panel can't be hidden
46170     },
46171     
46172     getMinWidth: function(){
46173         return this.minWidth;
46174     },
46175     
46176     getMinHeight: function(){
46177         return this.minHeight;
46178     }
46179 });
46180
46181
46182 Roo.NorthLayoutRegion = function(mgr, config){
46183     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46184     if(this.split){
46185         this.split.placement = Roo.SplitBar.TOP;
46186         this.split.orientation = Roo.SplitBar.VERTICAL;
46187         this.split.el.addClass("x-layout-split-v");
46188     }
46189     var size = config.initialSize || config.height;
46190     if(typeof size != "undefined"){
46191         this.el.setHeight(size);
46192     }
46193 };
46194 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46195     orientation: Roo.SplitBar.VERTICAL,
46196     getBox : function(){
46197         if(this.collapsed){
46198             return this.collapsedEl.getBox();
46199         }
46200         var box = this.el.getBox();
46201         if(this.split){
46202             box.height += this.split.el.getHeight();
46203         }
46204         return box;
46205     },
46206     
46207     updateBox : function(box){
46208         if(this.split && !this.collapsed){
46209             box.height -= this.split.el.getHeight();
46210             this.split.el.setLeft(box.x);
46211             this.split.el.setTop(box.y+box.height);
46212             this.split.el.setWidth(box.width);
46213         }
46214         if(this.collapsed){
46215             this.updateBody(box.width, null);
46216         }
46217         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46218     }
46219 });
46220
46221 Roo.SouthLayoutRegion = function(mgr, config){
46222     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46223     if(this.split){
46224         this.split.placement = Roo.SplitBar.BOTTOM;
46225         this.split.orientation = Roo.SplitBar.VERTICAL;
46226         this.split.el.addClass("x-layout-split-v");
46227     }
46228     var size = config.initialSize || config.height;
46229     if(typeof size != "undefined"){
46230         this.el.setHeight(size);
46231     }
46232 };
46233 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46234     orientation: Roo.SplitBar.VERTICAL,
46235     getBox : function(){
46236         if(this.collapsed){
46237             return this.collapsedEl.getBox();
46238         }
46239         var box = this.el.getBox();
46240         if(this.split){
46241             var sh = this.split.el.getHeight();
46242             box.height += sh;
46243             box.y -= sh;
46244         }
46245         return box;
46246     },
46247     
46248     updateBox : function(box){
46249         if(this.split && !this.collapsed){
46250             var sh = this.split.el.getHeight();
46251             box.height -= sh;
46252             box.y += sh;
46253             this.split.el.setLeft(box.x);
46254             this.split.el.setTop(box.y-sh);
46255             this.split.el.setWidth(box.width);
46256         }
46257         if(this.collapsed){
46258             this.updateBody(box.width, null);
46259         }
46260         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46261     }
46262 });
46263
46264 Roo.EastLayoutRegion = function(mgr, config){
46265     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46266     if(this.split){
46267         this.split.placement = Roo.SplitBar.RIGHT;
46268         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46269         this.split.el.addClass("x-layout-split-h");
46270     }
46271     var size = config.initialSize || config.width;
46272     if(typeof size != "undefined"){
46273         this.el.setWidth(size);
46274     }
46275 };
46276 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46277     orientation: Roo.SplitBar.HORIZONTAL,
46278     getBox : function(){
46279         if(this.collapsed){
46280             return this.collapsedEl.getBox();
46281         }
46282         var box = this.el.getBox();
46283         if(this.split){
46284             var sw = this.split.el.getWidth();
46285             box.width += sw;
46286             box.x -= sw;
46287         }
46288         return box;
46289     },
46290
46291     updateBox : function(box){
46292         if(this.split && !this.collapsed){
46293             var sw = this.split.el.getWidth();
46294             box.width -= sw;
46295             this.split.el.setLeft(box.x);
46296             this.split.el.setTop(box.y);
46297             this.split.el.setHeight(box.height);
46298             box.x += sw;
46299         }
46300         if(this.collapsed){
46301             this.updateBody(null, box.height);
46302         }
46303         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46304     }
46305 });
46306
46307 Roo.WestLayoutRegion = function(mgr, config){
46308     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46309     if(this.split){
46310         this.split.placement = Roo.SplitBar.LEFT;
46311         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46312         this.split.el.addClass("x-layout-split-h");
46313     }
46314     var size = config.initialSize || config.width;
46315     if(typeof size != "undefined"){
46316         this.el.setWidth(size);
46317     }
46318 };
46319 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46320     orientation: Roo.SplitBar.HORIZONTAL,
46321     getBox : function(){
46322         if(this.collapsed){
46323             return this.collapsedEl.getBox();
46324         }
46325         var box = this.el.getBox();
46326         if(this.split){
46327             box.width += this.split.el.getWidth();
46328         }
46329         return box;
46330     },
46331     
46332     updateBox : function(box){
46333         if(this.split && !this.collapsed){
46334             var sw = this.split.el.getWidth();
46335             box.width -= sw;
46336             this.split.el.setLeft(box.x+box.width);
46337             this.split.el.setTop(box.y);
46338             this.split.el.setHeight(box.height);
46339         }
46340         if(this.collapsed){
46341             this.updateBody(null, box.height);
46342         }
46343         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46344     }
46345 });
46346 /*
46347  * Based on:
46348  * Ext JS Library 1.1.1
46349  * Copyright(c) 2006-2007, Ext JS, LLC.
46350  *
46351  * Originally Released Under LGPL - original licence link has changed is not relivant.
46352  *
46353  * Fork - LGPL
46354  * <script type="text/javascript">
46355  */
46356  
46357  
46358 /*
46359  * Private internal class for reading and applying state
46360  */
46361 Roo.LayoutStateManager = function(layout){
46362      // default empty state
46363      this.state = {
46364         north: {},
46365         south: {},
46366         east: {},
46367         west: {}       
46368     };
46369 };
46370
46371 Roo.LayoutStateManager.prototype = {
46372     init : function(layout, provider){
46373         this.provider = provider;
46374         var state = provider.get(layout.id+"-layout-state");
46375         if(state){
46376             var wasUpdating = layout.isUpdating();
46377             if(!wasUpdating){
46378                 layout.beginUpdate();
46379             }
46380             for(var key in state){
46381                 if(typeof state[key] != "function"){
46382                     var rstate = state[key];
46383                     var r = layout.getRegion(key);
46384                     if(r && rstate){
46385                         if(rstate.size){
46386                             r.resizeTo(rstate.size);
46387                         }
46388                         if(rstate.collapsed == true){
46389                             r.collapse(true);
46390                         }else{
46391                             r.expand(null, true);
46392                         }
46393                     }
46394                 }
46395             }
46396             if(!wasUpdating){
46397                 layout.endUpdate();
46398             }
46399             this.state = state; 
46400         }
46401         this.layout = layout;
46402         layout.on("regionresized", this.onRegionResized, this);
46403         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46404         layout.on("regionexpanded", this.onRegionExpanded, this);
46405     },
46406     
46407     storeState : function(){
46408         this.provider.set(this.layout.id+"-layout-state", this.state);
46409     },
46410     
46411     onRegionResized : function(region, newSize){
46412         this.state[region.getPosition()].size = newSize;
46413         this.storeState();
46414     },
46415     
46416     onRegionCollapsed : function(region){
46417         this.state[region.getPosition()].collapsed = true;
46418         this.storeState();
46419     },
46420     
46421     onRegionExpanded : function(region){
46422         this.state[region.getPosition()].collapsed = false;
46423         this.storeState();
46424     }
46425 };/*
46426  * Based on:
46427  * Ext JS Library 1.1.1
46428  * Copyright(c) 2006-2007, Ext JS, LLC.
46429  *
46430  * Originally Released Under LGPL - original licence link has changed is not relivant.
46431  *
46432  * Fork - LGPL
46433  * <script type="text/javascript">
46434  */
46435 /**
46436  * @class Roo.ContentPanel
46437  * @extends Roo.util.Observable
46438  * A basic ContentPanel element.
46439  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46440  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46441  * @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
46442  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46443  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46444  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46445  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46446  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46447  * @cfg {String} title          The title for this panel
46448  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46449  * @cfg {String} url            Calls {@link #setUrl} with this value
46450  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46451  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46452  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46453  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46454
46455  * @constructor
46456  * Create a new ContentPanel.
46457  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46458  * @param {String/Object} config A string to set only the title or a config object
46459  * @param {String} content (optional) Set the HTML content for this panel
46460  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46461  */
46462 Roo.ContentPanel = function(el, config, content){
46463     
46464      
46465     /*
46466     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46467         config = el;
46468         el = Roo.id();
46469     }
46470     if (config && config.parentLayout) { 
46471         el = config.parentLayout.el.createChild(); 
46472     }
46473     */
46474     if(el.autoCreate){ // xtype is available if this is called from factory
46475         config = el;
46476         el = Roo.id();
46477     }
46478     this.el = Roo.get(el);
46479     if(!this.el && config && config.autoCreate){
46480         if(typeof config.autoCreate == "object"){
46481             if(!config.autoCreate.id){
46482                 config.autoCreate.id = config.id||el;
46483             }
46484             this.el = Roo.DomHelper.append(document.body,
46485                         config.autoCreate, true);
46486         }else{
46487             this.el = Roo.DomHelper.append(document.body,
46488                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46489         }
46490     }
46491     this.closable = false;
46492     this.loaded = false;
46493     this.active = false;
46494     if(typeof config == "string"){
46495         this.title = config;
46496     }else{
46497         Roo.apply(this, config);
46498     }
46499     
46500     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46501         this.wrapEl = this.el.wrap();
46502         this.toolbar.container = this.el.insertSibling(false, 'before');
46503         this.toolbar = new Roo.Toolbar(this.toolbar);
46504     }
46505     
46506     
46507     
46508     if(this.resizeEl){
46509         this.resizeEl = Roo.get(this.resizeEl, true);
46510     }else{
46511         this.resizeEl = this.el;
46512     }
46513     this.addEvents({
46514         /**
46515          * @event activate
46516          * Fires when this panel is activated. 
46517          * @param {Roo.ContentPanel} this
46518          */
46519         "activate" : true,
46520         /**
46521          * @event deactivate
46522          * Fires when this panel is activated. 
46523          * @param {Roo.ContentPanel} this
46524          */
46525         "deactivate" : true,
46526
46527         /**
46528          * @event resize
46529          * Fires when this panel is resized if fitToFrame is true.
46530          * @param {Roo.ContentPanel} this
46531          * @param {Number} width The width after any component adjustments
46532          * @param {Number} height The height after any component adjustments
46533          */
46534         "resize" : true,
46535         
46536          /**
46537          * @event render
46538          * Fires when this tab is created
46539          * @param {Roo.ContentPanel} this
46540          */
46541         "render" : true
46542         
46543         
46544         
46545     });
46546     if(this.autoScroll){
46547         this.resizeEl.setStyle("overflow", "auto");
46548     } else {
46549         // fix randome scrolling
46550         this.el.on('scroll', function() {
46551             Roo.log('fix random scolling');
46552             this.scrollTo('top',0); 
46553         });
46554     }
46555     content = content || this.content;
46556     if(content){
46557         this.setContent(content);
46558     }
46559     if(config && config.url){
46560         this.setUrl(this.url, this.params, this.loadOnce);
46561     }
46562     
46563     
46564     
46565     Roo.ContentPanel.superclass.constructor.call(this);
46566     
46567     this.fireEvent('render', this);
46568 };
46569
46570 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46571     tabTip:'',
46572     setRegion : function(region){
46573         this.region = region;
46574         if(region){
46575            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46576         }else{
46577            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46578         } 
46579     },
46580     
46581     /**
46582      * Returns the toolbar for this Panel if one was configured. 
46583      * @return {Roo.Toolbar} 
46584      */
46585     getToolbar : function(){
46586         return this.toolbar;
46587     },
46588     
46589     setActiveState : function(active){
46590         this.active = active;
46591         if(!active){
46592             this.fireEvent("deactivate", this);
46593         }else{
46594             this.fireEvent("activate", this);
46595         }
46596     },
46597     /**
46598      * Updates this panel's element
46599      * @param {String} content The new content
46600      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46601     */
46602     setContent : function(content, loadScripts){
46603         this.el.update(content, loadScripts);
46604     },
46605
46606     ignoreResize : function(w, h){
46607         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46608             return true;
46609         }else{
46610             this.lastSize = {width: w, height: h};
46611             return false;
46612         }
46613     },
46614     /**
46615      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46616      * @return {Roo.UpdateManager} The UpdateManager
46617      */
46618     getUpdateManager : function(){
46619         return this.el.getUpdateManager();
46620     },
46621      /**
46622      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46623      * @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:
46624 <pre><code>
46625 panel.load({
46626     url: "your-url.php",
46627     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46628     callback: yourFunction,
46629     scope: yourObject, //(optional scope)
46630     discardUrl: false,
46631     nocache: false,
46632     text: "Loading...",
46633     timeout: 30,
46634     scripts: false
46635 });
46636 </code></pre>
46637      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46638      * 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.
46639      * @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}
46640      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46641      * @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.
46642      * @return {Roo.ContentPanel} this
46643      */
46644     load : function(){
46645         var um = this.el.getUpdateManager();
46646         um.update.apply(um, arguments);
46647         return this;
46648     },
46649
46650
46651     /**
46652      * 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.
46653      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46654      * @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)
46655      * @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)
46656      * @return {Roo.UpdateManager} The UpdateManager
46657      */
46658     setUrl : function(url, params, loadOnce){
46659         if(this.refreshDelegate){
46660             this.removeListener("activate", this.refreshDelegate);
46661         }
46662         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46663         this.on("activate", this.refreshDelegate);
46664         return this.el.getUpdateManager();
46665     },
46666     
46667     _handleRefresh : function(url, params, loadOnce){
46668         if(!loadOnce || !this.loaded){
46669             var updater = this.el.getUpdateManager();
46670             updater.update(url, params, this._setLoaded.createDelegate(this));
46671         }
46672     },
46673     
46674     _setLoaded : function(){
46675         this.loaded = true;
46676     }, 
46677     
46678     /**
46679      * Returns this panel's id
46680      * @return {String} 
46681      */
46682     getId : function(){
46683         return this.el.id;
46684     },
46685     
46686     /** 
46687      * Returns this panel's element - used by regiosn to add.
46688      * @return {Roo.Element} 
46689      */
46690     getEl : function(){
46691         return this.wrapEl || this.el;
46692     },
46693     
46694     adjustForComponents : function(width, height){
46695         if(this.resizeEl != this.el){
46696             width -= this.el.getFrameWidth('lr');
46697             height -= this.el.getFrameWidth('tb');
46698         }
46699         if(this.toolbar){
46700             var te = this.toolbar.getEl();
46701             height -= te.getHeight();
46702             te.setWidth(width);
46703         }
46704         if(this.adjustments){
46705             width += this.adjustments[0];
46706             height += this.adjustments[1];
46707         }
46708         return {"width": width, "height": height};
46709     },
46710     
46711     setSize : function(width, height){
46712         if(this.fitToFrame && !this.ignoreResize(width, height)){
46713             if(this.fitContainer && this.resizeEl != this.el){
46714                 this.el.setSize(width, height);
46715             }
46716             var size = this.adjustForComponents(width, height);
46717             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46718             this.fireEvent('resize', this, size.width, size.height);
46719         }
46720     },
46721     
46722     /**
46723      * Returns this panel's title
46724      * @return {String} 
46725      */
46726     getTitle : function(){
46727         return this.title;
46728     },
46729     
46730     /**
46731      * Set this panel's title
46732      * @param {String} title
46733      */
46734     setTitle : function(title){
46735         this.title = title;
46736         if(this.region){
46737             this.region.updatePanelTitle(this, title);
46738         }
46739     },
46740     
46741     /**
46742      * Returns true is this panel was configured to be closable
46743      * @return {Boolean} 
46744      */
46745     isClosable : function(){
46746         return this.closable;
46747     },
46748     
46749     beforeSlide : function(){
46750         this.el.clip();
46751         this.resizeEl.clip();
46752     },
46753     
46754     afterSlide : function(){
46755         this.el.unclip();
46756         this.resizeEl.unclip();
46757     },
46758     
46759     /**
46760      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46761      *   Will fail silently if the {@link #setUrl} method has not been called.
46762      *   This does not activate the panel, just updates its content.
46763      */
46764     refresh : function(){
46765         if(this.refreshDelegate){
46766            this.loaded = false;
46767            this.refreshDelegate();
46768         }
46769     },
46770     
46771     /**
46772      * Destroys this panel
46773      */
46774     destroy : function(){
46775         this.el.removeAllListeners();
46776         var tempEl = document.createElement("span");
46777         tempEl.appendChild(this.el.dom);
46778         tempEl.innerHTML = "";
46779         this.el.remove();
46780         this.el = null;
46781     },
46782     
46783     /**
46784      * form - if the content panel contains a form - this is a reference to it.
46785      * @type {Roo.form.Form}
46786      */
46787     form : false,
46788     /**
46789      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46790      *    This contains a reference to it.
46791      * @type {Roo.View}
46792      */
46793     view : false,
46794     
46795       /**
46796      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46797      * <pre><code>
46798
46799 layout.addxtype({
46800        xtype : 'Form',
46801        items: [ .... ]
46802    }
46803 );
46804
46805 </code></pre>
46806      * @param {Object} cfg Xtype definition of item to add.
46807      */
46808     
46809     addxtype : function(cfg) {
46810         // add form..
46811         if (cfg.xtype.match(/^Form$/)) {
46812             var el = this.el.createChild();
46813
46814             this.form = new  Roo.form.Form(cfg);
46815             
46816             
46817             if ( this.form.allItems.length) this.form.render(el.dom);
46818             return this.form;
46819         }
46820         // should only have one of theses..
46821         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46822             // views..
46823             cfg.el = this.el.appendChild(document.createElement("div"));
46824             // factory?
46825             
46826             var ret = new Roo.factory(cfg);
46827             ret.render && ret.render(false, ''); // render blank..
46828             this.view = ret;
46829             return ret;
46830         }
46831         return false;
46832     }
46833 });
46834
46835 /**
46836  * @class Roo.GridPanel
46837  * @extends Roo.ContentPanel
46838  * @constructor
46839  * Create a new GridPanel.
46840  * @param {Roo.grid.Grid} grid The grid for this panel
46841  * @param {String/Object} config A string to set only the panel's title, or a config object
46842  */
46843 Roo.GridPanel = function(grid, config){
46844     
46845   
46846     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46847         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46848         
46849     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46850     
46851     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46852     
46853     if(this.toolbar){
46854         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46855     }
46856     // xtype created footer. - not sure if will work as we normally have to render first..
46857     if (this.footer && !this.footer.el && this.footer.xtype) {
46858         
46859         this.footer.container = this.grid.getView().getFooterPanel(true);
46860         this.footer.dataSource = this.grid.dataSource;
46861         this.footer = Roo.factory(this.footer, Roo);
46862         
46863     }
46864     
46865     grid.monitorWindowResize = false; // turn off autosizing
46866     grid.autoHeight = false;
46867     grid.autoWidth = false;
46868     this.grid = grid;
46869     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46870 };
46871
46872 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46873     getId : function(){
46874         return this.grid.id;
46875     },
46876     
46877     /**
46878      * Returns the grid for this panel
46879      * @return {Roo.grid.Grid} 
46880      */
46881     getGrid : function(){
46882         return this.grid;    
46883     },
46884     
46885     setSize : function(width, height){
46886         if(!this.ignoreResize(width, height)){
46887             var grid = this.grid;
46888             var size = this.adjustForComponents(width, height);
46889             grid.getGridEl().setSize(size.width, size.height);
46890             grid.autoSize();
46891         }
46892     },
46893     
46894     beforeSlide : function(){
46895         this.grid.getView().scroller.clip();
46896     },
46897     
46898     afterSlide : function(){
46899         this.grid.getView().scroller.unclip();
46900     },
46901     
46902     destroy : function(){
46903         this.grid.destroy();
46904         delete this.grid;
46905         Roo.GridPanel.superclass.destroy.call(this); 
46906     }
46907 });
46908
46909
46910 /**
46911  * @class Roo.NestedLayoutPanel
46912  * @extends Roo.ContentPanel
46913  * @constructor
46914  * Create a new NestedLayoutPanel.
46915  * 
46916  * 
46917  * @param {Roo.BorderLayout} layout The layout for this panel
46918  * @param {String/Object} config A string to set only the title or a config object
46919  */
46920 Roo.NestedLayoutPanel = function(layout, config)
46921 {
46922     // construct with only one argument..
46923     /* FIXME - implement nicer consturctors
46924     if (layout.layout) {
46925         config = layout;
46926         layout = config.layout;
46927         delete config.layout;
46928     }
46929     if (layout.xtype && !layout.getEl) {
46930         // then layout needs constructing..
46931         layout = Roo.factory(layout, Roo);
46932     }
46933     */
46934     
46935     
46936     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46937     
46938     layout.monitorWindowResize = false; // turn off autosizing
46939     this.layout = layout;
46940     this.layout.getEl().addClass("x-layout-nested-layout");
46941     
46942     
46943     
46944     
46945 };
46946
46947 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46948
46949     setSize : function(width, height){
46950         if(!this.ignoreResize(width, height)){
46951             var size = this.adjustForComponents(width, height);
46952             var el = this.layout.getEl();
46953             el.setSize(size.width, size.height);
46954             var touch = el.dom.offsetWidth;
46955             this.layout.layout();
46956             // ie requires a double layout on the first pass
46957             if(Roo.isIE && !this.initialized){
46958                 this.initialized = true;
46959                 this.layout.layout();
46960             }
46961         }
46962     },
46963     
46964     // activate all subpanels if not currently active..
46965     
46966     setActiveState : function(active){
46967         this.active = active;
46968         if(!active){
46969             this.fireEvent("deactivate", this);
46970             return;
46971         }
46972         
46973         this.fireEvent("activate", this);
46974         // not sure if this should happen before or after..
46975         if (!this.layout) {
46976             return; // should not happen..
46977         }
46978         var reg = false;
46979         for (var r in this.layout.regions) {
46980             reg = this.layout.getRegion(r);
46981             if (reg.getActivePanel()) {
46982                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46983                 reg.setActivePanel(reg.getActivePanel());
46984                 continue;
46985             }
46986             if (!reg.panels.length) {
46987                 continue;
46988             }
46989             reg.showPanel(reg.getPanel(0));
46990         }
46991         
46992         
46993         
46994         
46995     },
46996     
46997     /**
46998      * Returns the nested BorderLayout for this panel
46999      * @return {Roo.BorderLayout} 
47000      */
47001     getLayout : function(){
47002         return this.layout;
47003     },
47004     
47005      /**
47006      * Adds a xtype elements to the layout of the nested panel
47007      * <pre><code>
47008
47009 panel.addxtype({
47010        xtype : 'ContentPanel',
47011        region: 'west',
47012        items: [ .... ]
47013    }
47014 );
47015
47016 panel.addxtype({
47017         xtype : 'NestedLayoutPanel',
47018         region: 'west',
47019         layout: {
47020            center: { },
47021            west: { }   
47022         },
47023         items : [ ... list of content panels or nested layout panels.. ]
47024    }
47025 );
47026 </code></pre>
47027      * @param {Object} cfg Xtype definition of item to add.
47028      */
47029     addxtype : function(cfg) {
47030         return this.layout.addxtype(cfg);
47031     
47032     }
47033 });
47034
47035 Roo.ScrollPanel = function(el, config, content){
47036     config = config || {};
47037     config.fitToFrame = true;
47038     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47039     
47040     this.el.dom.style.overflow = "hidden";
47041     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47042     this.el.removeClass("x-layout-inactive-content");
47043     this.el.on("mousewheel", this.onWheel, this);
47044
47045     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47046     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47047     up.unselectable(); down.unselectable();
47048     up.on("click", this.scrollUp, this);
47049     down.on("click", this.scrollDown, this);
47050     up.addClassOnOver("x-scroller-btn-over");
47051     down.addClassOnOver("x-scroller-btn-over");
47052     up.addClassOnClick("x-scroller-btn-click");
47053     down.addClassOnClick("x-scroller-btn-click");
47054     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47055
47056     this.resizeEl = this.el;
47057     this.el = wrap; this.up = up; this.down = down;
47058 };
47059
47060 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47061     increment : 100,
47062     wheelIncrement : 5,
47063     scrollUp : function(){
47064         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47065     },
47066
47067     scrollDown : function(){
47068         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47069     },
47070
47071     afterScroll : function(){
47072         var el = this.resizeEl;
47073         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47074         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47075         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47076     },
47077
47078     setSize : function(){
47079         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47080         this.afterScroll();
47081     },
47082
47083     onWheel : function(e){
47084         var d = e.getWheelDelta();
47085         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47086         this.afterScroll();
47087         e.stopEvent();
47088     },
47089
47090     setContent : function(content, loadScripts){
47091         this.resizeEl.update(content, loadScripts);
47092     }
47093
47094 });
47095
47096
47097
47098
47099
47100
47101
47102
47103
47104 /**
47105  * @class Roo.TreePanel
47106  * @extends Roo.ContentPanel
47107  * @constructor
47108  * Create a new TreePanel. - defaults to fit/scoll contents.
47109  * @param {String/Object} config A string to set only the panel's title, or a config object
47110  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47111  */
47112 Roo.TreePanel = function(config){
47113     var el = config.el;
47114     var tree = config.tree;
47115     delete config.tree; 
47116     delete config.el; // hopefull!
47117     
47118     // wrapper for IE7 strict & safari scroll issue
47119     
47120     var treeEl = el.createChild();
47121     config.resizeEl = treeEl;
47122     
47123     
47124     
47125     Roo.TreePanel.superclass.constructor.call(this, el, config);
47126  
47127  
47128     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47129     //console.log(tree);
47130     this.on('activate', function()
47131     {
47132         if (this.tree.rendered) {
47133             return;
47134         }
47135         //console.log('render tree');
47136         this.tree.render();
47137     });
47138     
47139     this.on('resize',  function (cp, w, h) {
47140             this.tree.innerCt.setWidth(w);
47141             this.tree.innerCt.setHeight(h);
47142             this.tree.innerCt.setStyle('overflow-y', 'auto');
47143     });
47144
47145         
47146     
47147 };
47148
47149 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47150     fitToFrame : true,
47151     autoScroll : true
47152 });
47153
47154
47155
47156
47157
47158
47159
47160
47161
47162
47163
47164 /*
47165  * Based on:
47166  * Ext JS Library 1.1.1
47167  * Copyright(c) 2006-2007, Ext JS, LLC.
47168  *
47169  * Originally Released Under LGPL - original licence link has changed is not relivant.
47170  *
47171  * Fork - LGPL
47172  * <script type="text/javascript">
47173  */
47174  
47175
47176 /**
47177  * @class Roo.ReaderLayout
47178  * @extends Roo.BorderLayout
47179  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47180  * center region containing two nested regions (a top one for a list view and one for item preview below),
47181  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47182  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47183  * expedites the setup of the overall layout and regions for this common application style.
47184  * Example:
47185  <pre><code>
47186 var reader = new Roo.ReaderLayout();
47187 var CP = Roo.ContentPanel;  // shortcut for adding
47188
47189 reader.beginUpdate();
47190 reader.add("north", new CP("north", "North"));
47191 reader.add("west", new CP("west", {title: "West"}));
47192 reader.add("east", new CP("east", {title: "East"}));
47193
47194 reader.regions.listView.add(new CP("listView", "List"));
47195 reader.regions.preview.add(new CP("preview", "Preview"));
47196 reader.endUpdate();
47197 </code></pre>
47198 * @constructor
47199 * Create a new ReaderLayout
47200 * @param {Object} config Configuration options
47201 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47202 * document.body if omitted)
47203 */
47204 Roo.ReaderLayout = function(config, renderTo){
47205     var c = config || {size:{}};
47206     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47207         north: c.north !== false ? Roo.apply({
47208             split:false,
47209             initialSize: 32,
47210             titlebar: false
47211         }, c.north) : false,
47212         west: c.west !== false ? Roo.apply({
47213             split:true,
47214             initialSize: 200,
47215             minSize: 175,
47216             maxSize: 400,
47217             titlebar: true,
47218             collapsible: true,
47219             animate: true,
47220             margins:{left:5,right:0,bottom:5,top:5},
47221             cmargins:{left:5,right:5,bottom:5,top:5}
47222         }, c.west) : false,
47223         east: c.east !== false ? Roo.apply({
47224             split:true,
47225             initialSize: 200,
47226             minSize: 175,
47227             maxSize: 400,
47228             titlebar: true,
47229             collapsible: true,
47230             animate: true,
47231             margins:{left:0,right:5,bottom:5,top:5},
47232             cmargins:{left:5,right:5,bottom:5,top:5}
47233         }, c.east) : false,
47234         center: Roo.apply({
47235             tabPosition: 'top',
47236             autoScroll:false,
47237             closeOnTab: true,
47238             titlebar:false,
47239             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47240         }, c.center)
47241     });
47242
47243     this.el.addClass('x-reader');
47244
47245     this.beginUpdate();
47246
47247     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47248         south: c.preview !== false ? Roo.apply({
47249             split:true,
47250             initialSize: 200,
47251             minSize: 100,
47252             autoScroll:true,
47253             collapsible:true,
47254             titlebar: true,
47255             cmargins:{top:5,left:0, right:0, bottom:0}
47256         }, c.preview) : false,
47257         center: Roo.apply({
47258             autoScroll:false,
47259             titlebar:false,
47260             minHeight:200
47261         }, c.listView)
47262     });
47263     this.add('center', new Roo.NestedLayoutPanel(inner,
47264             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47265
47266     this.endUpdate();
47267
47268     this.regions.preview = inner.getRegion('south');
47269     this.regions.listView = inner.getRegion('center');
47270 };
47271
47272 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47273  * Based on:
47274  * Ext JS Library 1.1.1
47275  * Copyright(c) 2006-2007, Ext JS, LLC.
47276  *
47277  * Originally Released Under LGPL - original licence link has changed is not relivant.
47278  *
47279  * Fork - LGPL
47280  * <script type="text/javascript">
47281  */
47282  
47283 /**
47284  * @class Roo.grid.Grid
47285  * @extends Roo.util.Observable
47286  * This class represents the primary interface of a component based grid control.
47287  * <br><br>Usage:<pre><code>
47288  var grid = new Roo.grid.Grid("my-container-id", {
47289      ds: myDataStore,
47290      cm: myColModel,
47291      selModel: mySelectionModel,
47292      autoSizeColumns: true,
47293      monitorWindowResize: false,
47294      trackMouseOver: true
47295  });
47296  // set any options
47297  grid.render();
47298  * </code></pre>
47299  * <b>Common Problems:</b><br/>
47300  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47301  * element will correct this<br/>
47302  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47303  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47304  * are unpredictable.<br/>
47305  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47306  * grid to calculate dimensions/offsets.<br/>
47307   * @constructor
47308  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47309  * The container MUST have some type of size defined for the grid to fill. The container will be
47310  * automatically set to position relative if it isn't already.
47311  * @param {Object} config A config object that sets properties on this grid.
47312  */
47313 Roo.grid.Grid = function(container, config){
47314         // initialize the container
47315         this.container = Roo.get(container);
47316         this.container.update("");
47317         this.container.setStyle("overflow", "hidden");
47318     this.container.addClass('x-grid-container');
47319
47320     this.id = this.container.id;
47321
47322     Roo.apply(this, config);
47323     // check and correct shorthanded configs
47324     if(this.ds){
47325         this.dataSource = this.ds;
47326         delete this.ds;
47327     }
47328     if(this.cm){
47329         this.colModel = this.cm;
47330         delete this.cm;
47331     }
47332     if(this.sm){
47333         this.selModel = this.sm;
47334         delete this.sm;
47335     }
47336
47337     if (this.selModel) {
47338         this.selModel = Roo.factory(this.selModel, Roo.grid);
47339         this.sm = this.selModel;
47340         this.sm.xmodule = this.xmodule || false;
47341     }
47342     if (typeof(this.colModel.config) == 'undefined') {
47343         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47344         this.cm = this.colModel;
47345         this.cm.xmodule = this.xmodule || false;
47346     }
47347     if (this.dataSource) {
47348         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47349         this.ds = this.dataSource;
47350         this.ds.xmodule = this.xmodule || false;
47351          
47352     }
47353     
47354     
47355     
47356     if(this.width){
47357         this.container.setWidth(this.width);
47358     }
47359
47360     if(this.height){
47361         this.container.setHeight(this.height);
47362     }
47363     /** @private */
47364         this.addEvents({
47365         // raw events
47366         /**
47367          * @event click
47368          * The raw click event for the entire grid.
47369          * @param {Roo.EventObject} e
47370          */
47371         "click" : true,
47372         /**
47373          * @event dblclick
47374          * The raw dblclick event for the entire grid.
47375          * @param {Roo.EventObject} e
47376          */
47377         "dblclick" : true,
47378         /**
47379          * @event contextmenu
47380          * The raw contextmenu event for the entire grid.
47381          * @param {Roo.EventObject} e
47382          */
47383         "contextmenu" : true,
47384         /**
47385          * @event mousedown
47386          * The raw mousedown event for the entire grid.
47387          * @param {Roo.EventObject} e
47388          */
47389         "mousedown" : true,
47390         /**
47391          * @event mouseup
47392          * The raw mouseup event for the entire grid.
47393          * @param {Roo.EventObject} e
47394          */
47395         "mouseup" : true,
47396         /**
47397          * @event mouseover
47398          * The raw mouseover event for the entire grid.
47399          * @param {Roo.EventObject} e
47400          */
47401         "mouseover" : true,
47402         /**
47403          * @event mouseout
47404          * The raw mouseout event for the entire grid.
47405          * @param {Roo.EventObject} e
47406          */
47407         "mouseout" : true,
47408         /**
47409          * @event keypress
47410          * The raw keypress event for the entire grid.
47411          * @param {Roo.EventObject} e
47412          */
47413         "keypress" : true,
47414         /**
47415          * @event keydown
47416          * The raw keydown event for the entire grid.
47417          * @param {Roo.EventObject} e
47418          */
47419         "keydown" : true,
47420
47421         // custom events
47422
47423         /**
47424          * @event cellclick
47425          * Fires when a cell is clicked
47426          * @param {Grid} this
47427          * @param {Number} rowIndex
47428          * @param {Number} columnIndex
47429          * @param {Roo.EventObject} e
47430          */
47431         "cellclick" : true,
47432         /**
47433          * @event celldblclick
47434          * Fires when a cell is double clicked
47435          * @param {Grid} this
47436          * @param {Number} rowIndex
47437          * @param {Number} columnIndex
47438          * @param {Roo.EventObject} e
47439          */
47440         "celldblclick" : true,
47441         /**
47442          * @event rowclick
47443          * Fires when a row is clicked
47444          * @param {Grid} this
47445          * @param {Number} rowIndex
47446          * @param {Roo.EventObject} e
47447          */
47448         "rowclick" : true,
47449         /**
47450          * @event rowdblclick
47451          * Fires when a row is double clicked
47452          * @param {Grid} this
47453          * @param {Number} rowIndex
47454          * @param {Roo.EventObject} e
47455          */
47456         "rowdblclick" : true,
47457         /**
47458          * @event headerclick
47459          * Fires when a header is clicked
47460          * @param {Grid} this
47461          * @param {Number} columnIndex
47462          * @param {Roo.EventObject} e
47463          */
47464         "headerclick" : true,
47465         /**
47466          * @event headerdblclick
47467          * Fires when a header cell is double clicked
47468          * @param {Grid} this
47469          * @param {Number} columnIndex
47470          * @param {Roo.EventObject} e
47471          */
47472         "headerdblclick" : true,
47473         /**
47474          * @event rowcontextmenu
47475          * Fires when a row is right clicked
47476          * @param {Grid} this
47477          * @param {Number} rowIndex
47478          * @param {Roo.EventObject} e
47479          */
47480         "rowcontextmenu" : true,
47481         /**
47482          * @event cellcontextmenu
47483          * Fires when a cell is right clicked
47484          * @param {Grid} this
47485          * @param {Number} rowIndex
47486          * @param {Number} cellIndex
47487          * @param {Roo.EventObject} e
47488          */
47489          "cellcontextmenu" : true,
47490         /**
47491          * @event headercontextmenu
47492          * Fires when a header is right clicked
47493          * @param {Grid} this
47494          * @param {Number} columnIndex
47495          * @param {Roo.EventObject} e
47496          */
47497         "headercontextmenu" : true,
47498         /**
47499          * @event bodyscroll
47500          * Fires when the body element is scrolled
47501          * @param {Number} scrollLeft
47502          * @param {Number} scrollTop
47503          */
47504         "bodyscroll" : true,
47505         /**
47506          * @event columnresize
47507          * Fires when the user resizes a column
47508          * @param {Number} columnIndex
47509          * @param {Number} newSize
47510          */
47511         "columnresize" : true,
47512         /**
47513          * @event columnmove
47514          * Fires when the user moves a column
47515          * @param {Number} oldIndex
47516          * @param {Number} newIndex
47517          */
47518         "columnmove" : true,
47519         /**
47520          * @event startdrag
47521          * Fires when row(s) start being dragged
47522          * @param {Grid} this
47523          * @param {Roo.GridDD} dd The drag drop object
47524          * @param {event} e The raw browser event
47525          */
47526         "startdrag" : true,
47527         /**
47528          * @event enddrag
47529          * Fires when a drag operation is complete
47530          * @param {Grid} this
47531          * @param {Roo.GridDD} dd The drag drop object
47532          * @param {event} e The raw browser event
47533          */
47534         "enddrag" : true,
47535         /**
47536          * @event dragdrop
47537          * Fires when dragged row(s) are dropped on a valid DD target
47538          * @param {Grid} this
47539          * @param {Roo.GridDD} dd The drag drop object
47540          * @param {String} targetId The target drag drop object
47541          * @param {event} e The raw browser event
47542          */
47543         "dragdrop" : true,
47544         /**
47545          * @event dragover
47546          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47547          * @param {Grid} this
47548          * @param {Roo.GridDD} dd The drag drop object
47549          * @param {String} targetId The target drag drop object
47550          * @param {event} e The raw browser event
47551          */
47552         "dragover" : true,
47553         /**
47554          * @event dragenter
47555          *  Fires when the dragged row(s) first cross another DD target while being dragged
47556          * @param {Grid} this
47557          * @param {Roo.GridDD} dd The drag drop object
47558          * @param {String} targetId The target drag drop object
47559          * @param {event} e The raw browser event
47560          */
47561         "dragenter" : true,
47562         /**
47563          * @event dragout
47564          * Fires when the dragged row(s) leave another DD target while being dragged
47565          * @param {Grid} this
47566          * @param {Roo.GridDD} dd The drag drop object
47567          * @param {String} targetId The target drag drop object
47568          * @param {event} e The raw browser event
47569          */
47570         "dragout" : true,
47571         /**
47572          * @event rowclass
47573          * Fires when a row is rendered, so you can change add a style to it.
47574          * @param {GridView} gridview   The grid view
47575          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47576          */
47577         'rowclass' : true,
47578
47579         /**
47580          * @event render
47581          * Fires when the grid is rendered
47582          * @param {Grid} grid
47583          */
47584         'render' : true
47585     });
47586
47587     Roo.grid.Grid.superclass.constructor.call(this);
47588 };
47589 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47590     
47591     /**
47592      * @cfg {String} ddGroup - drag drop group.
47593      */
47594
47595     /**
47596      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47597      */
47598     minColumnWidth : 25,
47599
47600     /**
47601      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47602      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47603      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47604      */
47605     autoSizeColumns : false,
47606
47607     /**
47608      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47609      */
47610     autoSizeHeaders : true,
47611
47612     /**
47613      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47614      */
47615     monitorWindowResize : true,
47616
47617     /**
47618      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47619      * rows measured to get a columns size. Default is 0 (all rows).
47620      */
47621     maxRowsToMeasure : 0,
47622
47623     /**
47624      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47625      */
47626     trackMouseOver : true,
47627
47628     /**
47629     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47630     */
47631     
47632     /**
47633     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47634     */
47635     enableDragDrop : false,
47636     
47637     /**
47638     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47639     */
47640     enableColumnMove : true,
47641     
47642     /**
47643     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47644     */
47645     enableColumnHide : true,
47646     
47647     /**
47648     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47649     */
47650     enableRowHeightSync : false,
47651     
47652     /**
47653     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47654     */
47655     stripeRows : true,
47656     
47657     /**
47658     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47659     */
47660     autoHeight : false,
47661
47662     /**
47663      * @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.
47664      */
47665     autoExpandColumn : false,
47666
47667     /**
47668     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47669     * Default is 50.
47670     */
47671     autoExpandMin : 50,
47672
47673     /**
47674     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47675     */
47676     autoExpandMax : 1000,
47677
47678     /**
47679     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47680     */
47681     view : null,
47682
47683     /**
47684     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47685     */
47686     loadMask : false,
47687     /**
47688     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47689     */
47690     dropTarget: false,
47691     
47692    
47693     
47694     // private
47695     rendered : false,
47696
47697     /**
47698     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47699     * of a fixed width. Default is false.
47700     */
47701     /**
47702     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47703     */
47704     /**
47705      * Called once after all setup has been completed and the grid is ready to be rendered.
47706      * @return {Roo.grid.Grid} this
47707      */
47708     render : function()
47709     {
47710         var c = this.container;
47711         // try to detect autoHeight/width mode
47712         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47713             this.autoHeight = true;
47714         }
47715         var view = this.getView();
47716         view.init(this);
47717
47718         c.on("click", this.onClick, this);
47719         c.on("dblclick", this.onDblClick, this);
47720         c.on("contextmenu", this.onContextMenu, this);
47721         c.on("keydown", this.onKeyDown, this);
47722
47723         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47724
47725         this.getSelectionModel().init(this);
47726
47727         view.render();
47728
47729         if(this.loadMask){
47730             this.loadMask = new Roo.LoadMask(this.container,
47731                     Roo.apply({store:this.dataSource}, this.loadMask));
47732         }
47733         
47734         
47735         if (this.toolbar && this.toolbar.xtype) {
47736             this.toolbar.container = this.getView().getHeaderPanel(true);
47737             this.toolbar = new Roo.Toolbar(this.toolbar);
47738         }
47739         if (this.footer && this.footer.xtype) {
47740             this.footer.dataSource = this.getDataSource();
47741             this.footer.container = this.getView().getFooterPanel(true);
47742             this.footer = Roo.factory(this.footer, Roo);
47743         }
47744         if (this.dropTarget && this.dropTarget.xtype) {
47745             delete this.dropTarget.xtype;
47746             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47747         }
47748         
47749         
47750         this.rendered = true;
47751         this.fireEvent('render', this);
47752         return this;
47753     },
47754
47755         /**
47756          * Reconfigures the grid to use a different Store and Column Model.
47757          * The View will be bound to the new objects and refreshed.
47758          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47759          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47760          */
47761     reconfigure : function(dataSource, colModel){
47762         if(this.loadMask){
47763             this.loadMask.destroy();
47764             this.loadMask = new Roo.LoadMask(this.container,
47765                     Roo.apply({store:dataSource}, this.loadMask));
47766         }
47767         this.view.bind(dataSource, colModel);
47768         this.dataSource = dataSource;
47769         this.colModel = colModel;
47770         this.view.refresh(true);
47771     },
47772
47773     // private
47774     onKeyDown : function(e){
47775         this.fireEvent("keydown", e);
47776     },
47777
47778     /**
47779      * Destroy this grid.
47780      * @param {Boolean} removeEl True to remove the element
47781      */
47782     destroy : function(removeEl, keepListeners){
47783         if(this.loadMask){
47784             this.loadMask.destroy();
47785         }
47786         var c = this.container;
47787         c.removeAllListeners();
47788         this.view.destroy();
47789         this.colModel.purgeListeners();
47790         if(!keepListeners){
47791             this.purgeListeners();
47792         }
47793         c.update("");
47794         if(removeEl === true){
47795             c.remove();
47796         }
47797     },
47798
47799     // private
47800     processEvent : function(name, e){
47801         this.fireEvent(name, e);
47802         var t = e.getTarget();
47803         var v = this.view;
47804         var header = v.findHeaderIndex(t);
47805         if(header !== false){
47806             this.fireEvent("header" + name, this, header, e);
47807         }else{
47808             var row = v.findRowIndex(t);
47809             var cell = v.findCellIndex(t);
47810             if(row !== false){
47811                 this.fireEvent("row" + name, this, row, e);
47812                 if(cell !== false){
47813                     this.fireEvent("cell" + name, this, row, cell, e);
47814                 }
47815             }
47816         }
47817     },
47818
47819     // private
47820     onClick : function(e){
47821         this.processEvent("click", e);
47822     },
47823
47824     // private
47825     onContextMenu : function(e, t){
47826         this.processEvent("contextmenu", e);
47827     },
47828
47829     // private
47830     onDblClick : function(e){
47831         this.processEvent("dblclick", e);
47832     },
47833
47834     // private
47835     walkCells : function(row, col, step, fn, scope){
47836         var cm = this.colModel, clen = cm.getColumnCount();
47837         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47838         if(step < 0){
47839             if(col < 0){
47840                 row--;
47841                 first = false;
47842             }
47843             while(row >= 0){
47844                 if(!first){
47845                     col = clen-1;
47846                 }
47847                 first = false;
47848                 while(col >= 0){
47849                     if(fn.call(scope || this, row, col, cm) === true){
47850                         return [row, col];
47851                     }
47852                     col--;
47853                 }
47854                 row--;
47855             }
47856         } else {
47857             if(col >= clen){
47858                 row++;
47859                 first = false;
47860             }
47861             while(row < rlen){
47862                 if(!first){
47863                     col = 0;
47864                 }
47865                 first = false;
47866                 while(col < clen){
47867                     if(fn.call(scope || this, row, col, cm) === true){
47868                         return [row, col];
47869                     }
47870                     col++;
47871                 }
47872                 row++;
47873             }
47874         }
47875         return null;
47876     },
47877
47878     // private
47879     getSelections : function(){
47880         return this.selModel.getSelections();
47881     },
47882
47883     /**
47884      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47885      * but if manual update is required this method will initiate it.
47886      */
47887     autoSize : function(){
47888         if(this.rendered){
47889             this.view.layout();
47890             if(this.view.adjustForScroll){
47891                 this.view.adjustForScroll();
47892             }
47893         }
47894     },
47895
47896     /**
47897      * Returns the grid's underlying element.
47898      * @return {Element} The element
47899      */
47900     getGridEl : function(){
47901         return this.container;
47902     },
47903
47904     // private for compatibility, overridden by editor grid
47905     stopEditing : function(){},
47906
47907     /**
47908      * Returns the grid's SelectionModel.
47909      * @return {SelectionModel}
47910      */
47911     getSelectionModel : function(){
47912         if(!this.selModel){
47913             this.selModel = new Roo.grid.RowSelectionModel();
47914         }
47915         return this.selModel;
47916     },
47917
47918     /**
47919      * Returns the grid's DataSource.
47920      * @return {DataSource}
47921      */
47922     getDataSource : function(){
47923         return this.dataSource;
47924     },
47925
47926     /**
47927      * Returns the grid's ColumnModel.
47928      * @return {ColumnModel}
47929      */
47930     getColumnModel : function(){
47931         return this.colModel;
47932     },
47933
47934     /**
47935      * Returns the grid's GridView object.
47936      * @return {GridView}
47937      */
47938     getView : function(){
47939         if(!this.view){
47940             this.view = new Roo.grid.GridView(this.viewConfig);
47941         }
47942         return this.view;
47943     },
47944     /**
47945      * Called to get grid's drag proxy text, by default returns this.ddText.
47946      * @return {String}
47947      */
47948     getDragDropText : function(){
47949         var count = this.selModel.getCount();
47950         return String.format(this.ddText, count, count == 1 ? '' : 's');
47951     }
47952 });
47953 /**
47954  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47955  * %0 is replaced with the number of selected rows.
47956  * @type String
47957  */
47958 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47959  * Based on:
47960  * Ext JS Library 1.1.1
47961  * Copyright(c) 2006-2007, Ext JS, LLC.
47962  *
47963  * Originally Released Under LGPL - original licence link has changed is not relivant.
47964  *
47965  * Fork - LGPL
47966  * <script type="text/javascript">
47967  */
47968  
47969 Roo.grid.AbstractGridView = function(){
47970         this.grid = null;
47971         
47972         this.events = {
47973             "beforerowremoved" : true,
47974             "beforerowsinserted" : true,
47975             "beforerefresh" : true,
47976             "rowremoved" : true,
47977             "rowsinserted" : true,
47978             "rowupdated" : true,
47979             "refresh" : true
47980         };
47981     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47982 };
47983
47984 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47985     rowClass : "x-grid-row",
47986     cellClass : "x-grid-cell",
47987     tdClass : "x-grid-td",
47988     hdClass : "x-grid-hd",
47989     splitClass : "x-grid-hd-split",
47990     
47991         init: function(grid){
47992         this.grid = grid;
47993                 var cid = this.grid.getGridEl().id;
47994         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47995         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47996         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47997         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47998         },
47999         
48000         getColumnRenderers : function(){
48001         var renderers = [];
48002         var cm = this.grid.colModel;
48003         var colCount = cm.getColumnCount();
48004         for(var i = 0; i < colCount; i++){
48005             renderers[i] = cm.getRenderer(i);
48006         }
48007         return renderers;
48008     },
48009     
48010     getColumnIds : function(){
48011         var ids = [];
48012         var cm = this.grid.colModel;
48013         var colCount = cm.getColumnCount();
48014         for(var i = 0; i < colCount; i++){
48015             ids[i] = cm.getColumnId(i);
48016         }
48017         return ids;
48018     },
48019     
48020     getDataIndexes : function(){
48021         if(!this.indexMap){
48022             this.indexMap = this.buildIndexMap();
48023         }
48024         return this.indexMap.colToData;
48025     },
48026     
48027     getColumnIndexByDataIndex : function(dataIndex){
48028         if(!this.indexMap){
48029             this.indexMap = this.buildIndexMap();
48030         }
48031         return this.indexMap.dataToCol[dataIndex];
48032     },
48033     
48034     /**
48035      * Set a css style for a column dynamically. 
48036      * @param {Number} colIndex The index of the column
48037      * @param {String} name The css property name
48038      * @param {String} value The css value
48039      */
48040     setCSSStyle : function(colIndex, name, value){
48041         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48042         Roo.util.CSS.updateRule(selector, name, value);
48043     },
48044     
48045     generateRules : function(cm){
48046         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48047         Roo.util.CSS.removeStyleSheet(rulesId);
48048         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48049             var cid = cm.getColumnId(i);
48050             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48051                          this.tdSelector, cid, " {\n}\n",
48052                          this.hdSelector, cid, " {\n}\n",
48053                          this.splitSelector, cid, " {\n}\n");
48054         }
48055         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48056     }
48057 });/*
48058  * Based on:
48059  * Ext JS Library 1.1.1
48060  * Copyright(c) 2006-2007, Ext JS, LLC.
48061  *
48062  * Originally Released Under LGPL - original licence link has changed is not relivant.
48063  *
48064  * Fork - LGPL
48065  * <script type="text/javascript">
48066  */
48067
48068 // private
48069 // This is a support class used internally by the Grid components
48070 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48071     this.grid = grid;
48072     this.view = grid.getView();
48073     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48074     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48075     if(hd2){
48076         this.setHandleElId(Roo.id(hd));
48077         this.setOuterHandleElId(Roo.id(hd2));
48078     }
48079     this.scroll = false;
48080 };
48081 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48082     maxDragWidth: 120,
48083     getDragData : function(e){
48084         var t = Roo.lib.Event.getTarget(e);
48085         var h = this.view.findHeaderCell(t);
48086         if(h){
48087             return {ddel: h.firstChild, header:h};
48088         }
48089         return false;
48090     },
48091
48092     onInitDrag : function(e){
48093         this.view.headersDisabled = true;
48094         var clone = this.dragData.ddel.cloneNode(true);
48095         clone.id = Roo.id();
48096         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48097         this.proxy.update(clone);
48098         return true;
48099     },
48100
48101     afterValidDrop : function(){
48102         var v = this.view;
48103         setTimeout(function(){
48104             v.headersDisabled = false;
48105         }, 50);
48106     },
48107
48108     afterInvalidDrop : function(){
48109         var v = this.view;
48110         setTimeout(function(){
48111             v.headersDisabled = false;
48112         }, 50);
48113     }
48114 });
48115 /*
48116  * Based on:
48117  * Ext JS Library 1.1.1
48118  * Copyright(c) 2006-2007, Ext JS, LLC.
48119  *
48120  * Originally Released Under LGPL - original licence link has changed is not relivant.
48121  *
48122  * Fork - LGPL
48123  * <script type="text/javascript">
48124  */
48125 // private
48126 // This is a support class used internally by the Grid components
48127 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48128     this.grid = grid;
48129     this.view = grid.getView();
48130     // split the proxies so they don't interfere with mouse events
48131     this.proxyTop = Roo.DomHelper.append(document.body, {
48132         cls:"col-move-top", html:"&#160;"
48133     }, true);
48134     this.proxyBottom = Roo.DomHelper.append(document.body, {
48135         cls:"col-move-bottom", html:"&#160;"
48136     }, true);
48137     this.proxyTop.hide = this.proxyBottom.hide = function(){
48138         this.setLeftTop(-100,-100);
48139         this.setStyle("visibility", "hidden");
48140     };
48141     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48142     // temporarily disabled
48143     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48144     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48145 };
48146 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48147     proxyOffsets : [-4, -9],
48148     fly: Roo.Element.fly,
48149
48150     getTargetFromEvent : function(e){
48151         var t = Roo.lib.Event.getTarget(e);
48152         var cindex = this.view.findCellIndex(t);
48153         if(cindex !== false){
48154             return this.view.getHeaderCell(cindex);
48155         }
48156         return null;
48157     },
48158
48159     nextVisible : function(h){
48160         var v = this.view, cm = this.grid.colModel;
48161         h = h.nextSibling;
48162         while(h){
48163             if(!cm.isHidden(v.getCellIndex(h))){
48164                 return h;
48165             }
48166             h = h.nextSibling;
48167         }
48168         return null;
48169     },
48170
48171     prevVisible : function(h){
48172         var v = this.view, cm = this.grid.colModel;
48173         h = h.prevSibling;
48174         while(h){
48175             if(!cm.isHidden(v.getCellIndex(h))){
48176                 return h;
48177             }
48178             h = h.prevSibling;
48179         }
48180         return null;
48181     },
48182
48183     positionIndicator : function(h, n, e){
48184         var x = Roo.lib.Event.getPageX(e);
48185         var r = Roo.lib.Dom.getRegion(n.firstChild);
48186         var px, pt, py = r.top + this.proxyOffsets[1];
48187         if((r.right - x) <= (r.right-r.left)/2){
48188             px = r.right+this.view.borderWidth;
48189             pt = "after";
48190         }else{
48191             px = r.left;
48192             pt = "before";
48193         }
48194         var oldIndex = this.view.getCellIndex(h);
48195         var newIndex = this.view.getCellIndex(n);
48196
48197         if(this.grid.colModel.isFixed(newIndex)){
48198             return false;
48199         }
48200
48201         var locked = this.grid.colModel.isLocked(newIndex);
48202
48203         if(pt == "after"){
48204             newIndex++;
48205         }
48206         if(oldIndex < newIndex){
48207             newIndex--;
48208         }
48209         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48210             return false;
48211         }
48212         px +=  this.proxyOffsets[0];
48213         this.proxyTop.setLeftTop(px, py);
48214         this.proxyTop.show();
48215         if(!this.bottomOffset){
48216             this.bottomOffset = this.view.mainHd.getHeight();
48217         }
48218         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48219         this.proxyBottom.show();
48220         return pt;
48221     },
48222
48223     onNodeEnter : function(n, dd, e, data){
48224         if(data.header != n){
48225             this.positionIndicator(data.header, n, e);
48226         }
48227     },
48228
48229     onNodeOver : function(n, dd, e, data){
48230         var result = false;
48231         if(data.header != n){
48232             result = this.positionIndicator(data.header, n, e);
48233         }
48234         if(!result){
48235             this.proxyTop.hide();
48236             this.proxyBottom.hide();
48237         }
48238         return result ? this.dropAllowed : this.dropNotAllowed;
48239     },
48240
48241     onNodeOut : function(n, dd, e, data){
48242         this.proxyTop.hide();
48243         this.proxyBottom.hide();
48244     },
48245
48246     onNodeDrop : function(n, dd, e, data){
48247         var h = data.header;
48248         if(h != n){
48249             var cm = this.grid.colModel;
48250             var x = Roo.lib.Event.getPageX(e);
48251             var r = Roo.lib.Dom.getRegion(n.firstChild);
48252             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48253             var oldIndex = this.view.getCellIndex(h);
48254             var newIndex = this.view.getCellIndex(n);
48255             var locked = cm.isLocked(newIndex);
48256             if(pt == "after"){
48257                 newIndex++;
48258             }
48259             if(oldIndex < newIndex){
48260                 newIndex--;
48261             }
48262             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48263                 return false;
48264             }
48265             cm.setLocked(oldIndex, locked, true);
48266             cm.moveColumn(oldIndex, newIndex);
48267             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48268             return true;
48269         }
48270         return false;
48271     }
48272 });
48273 /*
48274  * Based on:
48275  * Ext JS Library 1.1.1
48276  * Copyright(c) 2006-2007, Ext JS, LLC.
48277  *
48278  * Originally Released Under LGPL - original licence link has changed is not relivant.
48279  *
48280  * Fork - LGPL
48281  * <script type="text/javascript">
48282  */
48283   
48284 /**
48285  * @class Roo.grid.GridView
48286  * @extends Roo.util.Observable
48287  *
48288  * @constructor
48289  * @param {Object} config
48290  */
48291 Roo.grid.GridView = function(config){
48292     Roo.grid.GridView.superclass.constructor.call(this);
48293     this.el = null;
48294
48295     Roo.apply(this, config);
48296 };
48297
48298 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48299
48300     /**
48301      * Override this function to apply custom css classes to rows during rendering
48302      * @param {Record} record The record
48303      * @param {Number} index
48304      * @method getRowClass
48305      */
48306     rowClass : "x-grid-row",
48307
48308     cellClass : "x-grid-col",
48309
48310     tdClass : "x-grid-td",
48311
48312     hdClass : "x-grid-hd",
48313
48314     splitClass : "x-grid-split",
48315
48316     sortClasses : ["sort-asc", "sort-desc"],
48317
48318     enableMoveAnim : false,
48319
48320     hlColor: "C3DAF9",
48321
48322     dh : Roo.DomHelper,
48323
48324     fly : Roo.Element.fly,
48325
48326     css : Roo.util.CSS,
48327
48328     borderWidth: 1,
48329
48330     splitOffset: 3,
48331
48332     scrollIncrement : 22,
48333
48334     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48335
48336     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48337
48338     bind : function(ds, cm){
48339         if(this.ds){
48340             this.ds.un("load", this.onLoad, this);
48341             this.ds.un("datachanged", this.onDataChange, this);
48342             this.ds.un("add", this.onAdd, this);
48343             this.ds.un("remove", this.onRemove, this);
48344             this.ds.un("update", this.onUpdate, this);
48345             this.ds.un("clear", this.onClear, this);
48346         }
48347         if(ds){
48348             ds.on("load", this.onLoad, this);
48349             ds.on("datachanged", this.onDataChange, this);
48350             ds.on("add", this.onAdd, this);
48351             ds.on("remove", this.onRemove, this);
48352             ds.on("update", this.onUpdate, this);
48353             ds.on("clear", this.onClear, this);
48354         }
48355         this.ds = ds;
48356
48357         if(this.cm){
48358             this.cm.un("widthchange", this.onColWidthChange, this);
48359             this.cm.un("headerchange", this.onHeaderChange, this);
48360             this.cm.un("hiddenchange", this.onHiddenChange, this);
48361             this.cm.un("columnmoved", this.onColumnMove, this);
48362             this.cm.un("columnlockchange", this.onColumnLock, this);
48363         }
48364         if(cm){
48365             this.generateRules(cm);
48366             cm.on("widthchange", this.onColWidthChange, this);
48367             cm.on("headerchange", this.onHeaderChange, this);
48368             cm.on("hiddenchange", this.onHiddenChange, this);
48369             cm.on("columnmoved", this.onColumnMove, this);
48370             cm.on("columnlockchange", this.onColumnLock, this);
48371         }
48372         this.cm = cm;
48373     },
48374
48375     init: function(grid){
48376         Roo.grid.GridView.superclass.init.call(this, grid);
48377
48378         this.bind(grid.dataSource, grid.colModel);
48379
48380         grid.on("headerclick", this.handleHeaderClick, this);
48381
48382         if(grid.trackMouseOver){
48383             grid.on("mouseover", this.onRowOver, this);
48384             grid.on("mouseout", this.onRowOut, this);
48385         }
48386         grid.cancelTextSelection = function(){};
48387         this.gridId = grid.id;
48388
48389         var tpls = this.templates || {};
48390
48391         if(!tpls.master){
48392             tpls.master = new Roo.Template(
48393                '<div class="x-grid" hidefocus="true">',
48394                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48395                   '<div class="x-grid-topbar"></div>',
48396                   '<div class="x-grid-scroller"><div></div></div>',
48397                   '<div class="x-grid-locked">',
48398                       '<div class="x-grid-header">{lockedHeader}</div>',
48399                       '<div class="x-grid-body">{lockedBody}</div>',
48400                   "</div>",
48401                   '<div class="x-grid-viewport">',
48402                       '<div class="x-grid-header">{header}</div>',
48403                       '<div class="x-grid-body">{body}</div>',
48404                   "</div>",
48405                   '<div class="x-grid-bottombar"></div>',
48406                  
48407                   '<div class="x-grid-resize-proxy">&#160;</div>',
48408                "</div>"
48409             );
48410             tpls.master.disableformats = true;
48411         }
48412
48413         if(!tpls.header){
48414             tpls.header = new Roo.Template(
48415                '<table border="0" cellspacing="0" cellpadding="0">',
48416                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48417                "</table>{splits}"
48418             );
48419             tpls.header.disableformats = true;
48420         }
48421         tpls.header.compile();
48422
48423         if(!tpls.hcell){
48424             tpls.hcell = new Roo.Template(
48425                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48426                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48427                 "</div></td>"
48428              );
48429              tpls.hcell.disableFormats = true;
48430         }
48431         tpls.hcell.compile();
48432
48433         if(!tpls.hsplit){
48434             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48435             tpls.hsplit.disableFormats = true;
48436         }
48437         tpls.hsplit.compile();
48438
48439         if(!tpls.body){
48440             tpls.body = new Roo.Template(
48441                '<table border="0" cellspacing="0" cellpadding="0">',
48442                "<tbody>{rows}</tbody>",
48443                "</table>"
48444             );
48445             tpls.body.disableFormats = true;
48446         }
48447         tpls.body.compile();
48448
48449         if(!tpls.row){
48450             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48451             tpls.row.disableFormats = true;
48452         }
48453         tpls.row.compile();
48454
48455         if(!tpls.cell){
48456             tpls.cell = new Roo.Template(
48457                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48458                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48459                 "</td>"
48460             );
48461             tpls.cell.disableFormats = true;
48462         }
48463         tpls.cell.compile();
48464
48465         this.templates = tpls;
48466     },
48467
48468     // remap these for backwards compat
48469     onColWidthChange : function(){
48470         this.updateColumns.apply(this, arguments);
48471     },
48472     onHeaderChange : function(){
48473         this.updateHeaders.apply(this, arguments);
48474     }, 
48475     onHiddenChange : function(){
48476         this.handleHiddenChange.apply(this, arguments);
48477     },
48478     onColumnMove : function(){
48479         this.handleColumnMove.apply(this, arguments);
48480     },
48481     onColumnLock : function(){
48482         this.handleLockChange.apply(this, arguments);
48483     },
48484
48485     onDataChange : function(){
48486         this.refresh();
48487         this.updateHeaderSortState();
48488     },
48489
48490     onClear : function(){
48491         this.refresh();
48492     },
48493
48494     onUpdate : function(ds, record){
48495         this.refreshRow(record);
48496     },
48497
48498     refreshRow : function(record){
48499         var ds = this.ds, index;
48500         if(typeof record == 'number'){
48501             index = record;
48502             record = ds.getAt(index);
48503         }else{
48504             index = ds.indexOf(record);
48505         }
48506         this.insertRows(ds, index, index, true);
48507         this.onRemove(ds, record, index+1, true);
48508         this.syncRowHeights(index, index);
48509         this.layout();
48510         this.fireEvent("rowupdated", this, index, record);
48511     },
48512
48513     onAdd : function(ds, records, index){
48514         this.insertRows(ds, index, index + (records.length-1));
48515     },
48516
48517     onRemove : function(ds, record, index, isUpdate){
48518         if(isUpdate !== true){
48519             this.fireEvent("beforerowremoved", this, index, record);
48520         }
48521         var bt = this.getBodyTable(), lt = this.getLockedTable();
48522         if(bt.rows[index]){
48523             bt.firstChild.removeChild(bt.rows[index]);
48524         }
48525         if(lt.rows[index]){
48526             lt.firstChild.removeChild(lt.rows[index]);
48527         }
48528         if(isUpdate !== true){
48529             this.stripeRows(index);
48530             this.syncRowHeights(index, index);
48531             this.layout();
48532             this.fireEvent("rowremoved", this, index, record);
48533         }
48534     },
48535
48536     onLoad : function(){
48537         this.scrollToTop();
48538     },
48539
48540     /**
48541      * Scrolls the grid to the top
48542      */
48543     scrollToTop : function(){
48544         if(this.scroller){
48545             this.scroller.dom.scrollTop = 0;
48546             this.syncScroll();
48547         }
48548     },
48549
48550     /**
48551      * Gets a panel in the header of the grid that can be used for toolbars etc.
48552      * After modifying the contents of this panel a call to grid.autoSize() may be
48553      * required to register any changes in size.
48554      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48555      * @return Roo.Element
48556      */
48557     getHeaderPanel : function(doShow){
48558         if(doShow){
48559             this.headerPanel.show();
48560         }
48561         return this.headerPanel;
48562     },
48563
48564     /**
48565      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48566      * After modifying the contents of this panel a call to grid.autoSize() may be
48567      * required to register any changes in size.
48568      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48569      * @return Roo.Element
48570      */
48571     getFooterPanel : function(doShow){
48572         if(doShow){
48573             this.footerPanel.show();
48574         }
48575         return this.footerPanel;
48576     },
48577
48578     initElements : function(){
48579         var E = Roo.Element;
48580         var el = this.grid.getGridEl().dom.firstChild;
48581         var cs = el.childNodes;
48582
48583         this.el = new E(el);
48584         
48585          this.focusEl = new E(el.firstChild);
48586         this.focusEl.swallowEvent("click", true);
48587         
48588         this.headerPanel = new E(cs[1]);
48589         this.headerPanel.enableDisplayMode("block");
48590
48591         this.scroller = new E(cs[2]);
48592         this.scrollSizer = new E(this.scroller.dom.firstChild);
48593
48594         this.lockedWrap = new E(cs[3]);
48595         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48596         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48597
48598         this.mainWrap = new E(cs[4]);
48599         this.mainHd = new E(this.mainWrap.dom.firstChild);
48600         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48601
48602         this.footerPanel = new E(cs[5]);
48603         this.footerPanel.enableDisplayMode("block");
48604
48605         this.resizeProxy = new E(cs[6]);
48606
48607         this.headerSelector = String.format(
48608            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48609            this.lockedHd.id, this.mainHd.id
48610         );
48611
48612         this.splitterSelector = String.format(
48613            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48614            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48615         );
48616     },
48617     idToCssName : function(s)
48618     {
48619         return s.replace(/[^a-z0-9]+/ig, '-');
48620     },
48621
48622     getHeaderCell : function(index){
48623         return Roo.DomQuery.select(this.headerSelector)[index];
48624     },
48625
48626     getHeaderCellMeasure : function(index){
48627         return this.getHeaderCell(index).firstChild;
48628     },
48629
48630     getHeaderCellText : function(index){
48631         return this.getHeaderCell(index).firstChild.firstChild;
48632     },
48633
48634     getLockedTable : function(){
48635         return this.lockedBody.dom.firstChild;
48636     },
48637
48638     getBodyTable : function(){
48639         return this.mainBody.dom.firstChild;
48640     },
48641
48642     getLockedRow : function(index){
48643         return this.getLockedTable().rows[index];
48644     },
48645
48646     getRow : function(index){
48647         return this.getBodyTable().rows[index];
48648     },
48649
48650     getRowComposite : function(index){
48651         if(!this.rowEl){
48652             this.rowEl = new Roo.CompositeElementLite();
48653         }
48654         var els = [], lrow, mrow;
48655         if(lrow = this.getLockedRow(index)){
48656             els.push(lrow);
48657         }
48658         if(mrow = this.getRow(index)){
48659             els.push(mrow);
48660         }
48661         this.rowEl.elements = els;
48662         return this.rowEl;
48663     },
48664     /**
48665      * Gets the 'td' of the cell
48666      * 
48667      * @param {Integer} rowIndex row to select
48668      * @param {Integer} colIndex column to select
48669      * 
48670      * @return {Object} 
48671      */
48672     getCell : function(rowIndex, colIndex){
48673         var locked = this.cm.getLockedCount();
48674         var source;
48675         if(colIndex < locked){
48676             source = this.lockedBody.dom.firstChild;
48677         }else{
48678             source = this.mainBody.dom.firstChild;
48679             colIndex -= locked;
48680         }
48681         return source.rows[rowIndex].childNodes[colIndex];
48682     },
48683
48684     getCellText : function(rowIndex, colIndex){
48685         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48686     },
48687
48688     getCellBox : function(cell){
48689         var b = this.fly(cell).getBox();
48690         if(Roo.isOpera){ // opera fails to report the Y
48691             b.y = cell.offsetTop + this.mainBody.getY();
48692         }
48693         return b;
48694     },
48695
48696     getCellIndex : function(cell){
48697         var id = String(cell.className).match(this.cellRE);
48698         if(id){
48699             return parseInt(id[1], 10);
48700         }
48701         return 0;
48702     },
48703
48704     findHeaderIndex : function(n){
48705         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48706         return r ? this.getCellIndex(r) : false;
48707     },
48708
48709     findHeaderCell : function(n){
48710         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48711         return r ? r : false;
48712     },
48713
48714     findRowIndex : function(n){
48715         if(!n){
48716             return false;
48717         }
48718         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48719         return r ? r.rowIndex : false;
48720     },
48721
48722     findCellIndex : function(node){
48723         var stop = this.el.dom;
48724         while(node && node != stop){
48725             if(this.findRE.test(node.className)){
48726                 return this.getCellIndex(node);
48727             }
48728             node = node.parentNode;
48729         }
48730         return false;
48731     },
48732
48733     getColumnId : function(index){
48734         return this.cm.getColumnId(index);
48735     },
48736
48737     getSplitters : function()
48738     {
48739         if(this.splitterSelector){
48740            return Roo.DomQuery.select(this.splitterSelector);
48741         }else{
48742             return null;
48743       }
48744     },
48745
48746     getSplitter : function(index){
48747         return this.getSplitters()[index];
48748     },
48749
48750     onRowOver : function(e, t){
48751         var row;
48752         if((row = this.findRowIndex(t)) !== false){
48753             this.getRowComposite(row).addClass("x-grid-row-over");
48754         }
48755     },
48756
48757     onRowOut : function(e, t){
48758         var row;
48759         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48760             this.getRowComposite(row).removeClass("x-grid-row-over");
48761         }
48762     },
48763
48764     renderHeaders : function(){
48765         var cm = this.cm;
48766         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48767         var cb = [], lb = [], sb = [], lsb = [], p = {};
48768         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48769             p.cellId = "x-grid-hd-0-" + i;
48770             p.splitId = "x-grid-csplit-0-" + i;
48771             p.id = cm.getColumnId(i);
48772             p.title = cm.getColumnTooltip(i) || "";
48773             p.value = cm.getColumnHeader(i) || "";
48774             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48775             if(!cm.isLocked(i)){
48776                 cb[cb.length] = ct.apply(p);
48777                 sb[sb.length] = st.apply(p);
48778             }else{
48779                 lb[lb.length] = ct.apply(p);
48780                 lsb[lsb.length] = st.apply(p);
48781             }
48782         }
48783         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48784                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48785     },
48786
48787     updateHeaders : function(){
48788         var html = this.renderHeaders();
48789         this.lockedHd.update(html[0]);
48790         this.mainHd.update(html[1]);
48791     },
48792
48793     /**
48794      * Focuses the specified row.
48795      * @param {Number} row The row index
48796      */
48797     focusRow : function(row)
48798     {
48799         //Roo.log('GridView.focusRow');
48800         var x = this.scroller.dom.scrollLeft;
48801         this.focusCell(row, 0, false);
48802         this.scroller.dom.scrollLeft = x;
48803     },
48804
48805     /**
48806      * Focuses the specified cell.
48807      * @param {Number} row The row index
48808      * @param {Number} col The column index
48809      * @param {Boolean} hscroll false to disable horizontal scrolling
48810      */
48811     focusCell : function(row, col, hscroll)
48812     {
48813         //Roo.log('GridView.focusCell');
48814         var el = this.ensureVisible(row, col, hscroll);
48815         this.focusEl.alignTo(el, "tl-tl");
48816         if(Roo.isGecko){
48817             this.focusEl.focus();
48818         }else{
48819             this.focusEl.focus.defer(1, this.focusEl);
48820         }
48821     },
48822
48823     /**
48824      * Scrolls the specified cell into view
48825      * @param {Number} row The row index
48826      * @param {Number} col The column index
48827      * @param {Boolean} hscroll false to disable horizontal scrolling
48828      */
48829     ensureVisible : function(row, col, hscroll)
48830     {
48831         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48832         //return null; //disable for testing.
48833         if(typeof row != "number"){
48834             row = row.rowIndex;
48835         }
48836         if(row < 0 && row >= this.ds.getCount()){
48837             return  null;
48838         }
48839         col = (col !== undefined ? col : 0);
48840         var cm = this.grid.colModel;
48841         while(cm.isHidden(col)){
48842             col++;
48843         }
48844
48845         var el = this.getCell(row, col);
48846         if(!el){
48847             return null;
48848         }
48849         var c = this.scroller.dom;
48850
48851         var ctop = parseInt(el.offsetTop, 10);
48852         var cleft = parseInt(el.offsetLeft, 10);
48853         var cbot = ctop + el.offsetHeight;
48854         var cright = cleft + el.offsetWidth;
48855         
48856         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48857         var stop = parseInt(c.scrollTop, 10);
48858         var sleft = parseInt(c.scrollLeft, 10);
48859         var sbot = stop + ch;
48860         var sright = sleft + c.clientWidth;
48861         /*
48862         Roo.log('GridView.ensureVisible:' +
48863                 ' ctop:' + ctop +
48864                 ' c.clientHeight:' + c.clientHeight +
48865                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48866                 ' stop:' + stop +
48867                 ' cbot:' + cbot +
48868                 ' sbot:' + sbot +
48869                 ' ch:' + ch  
48870                 );
48871         */
48872         if(ctop < stop){
48873              c.scrollTop = ctop;
48874             //Roo.log("set scrolltop to ctop DISABLE?");
48875         }else if(cbot > sbot){
48876             //Roo.log("set scrolltop to cbot-ch");
48877             c.scrollTop = cbot-ch;
48878         }
48879         
48880         if(hscroll !== false){
48881             if(cleft < sleft){
48882                 c.scrollLeft = cleft;
48883             }else if(cright > sright){
48884                 c.scrollLeft = cright-c.clientWidth;
48885             }
48886         }
48887          
48888         return el;
48889     },
48890
48891     updateColumns : function(){
48892         this.grid.stopEditing();
48893         var cm = this.grid.colModel, colIds = this.getColumnIds();
48894         //var totalWidth = cm.getTotalWidth();
48895         var pos = 0;
48896         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48897             //if(cm.isHidden(i)) continue;
48898             var w = cm.getColumnWidth(i);
48899             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48900             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48901         }
48902         this.updateSplitters();
48903     },
48904
48905     generateRules : function(cm){
48906         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48907         Roo.util.CSS.removeStyleSheet(rulesId);
48908         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48909             var cid = cm.getColumnId(i);
48910             var align = '';
48911             if(cm.config[i].align){
48912                 align = 'text-align:'+cm.config[i].align+';';
48913             }
48914             var hidden = '';
48915             if(cm.isHidden(i)){
48916                 hidden = 'display:none;';
48917             }
48918             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48919             ruleBuf.push(
48920                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48921                     this.hdSelector, cid, " {\n", align, width, "}\n",
48922                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48923                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48924         }
48925         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48926     },
48927
48928     updateSplitters : function(){
48929         var cm = this.cm, s = this.getSplitters();
48930         if(s){ // splitters not created yet
48931             var pos = 0, locked = true;
48932             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48933                 if(cm.isHidden(i)) continue;
48934                 var w = cm.getColumnWidth(i); // make sure it's a number
48935                 if(!cm.isLocked(i) && locked){
48936                     pos = 0;
48937                     locked = false;
48938                 }
48939                 pos += w;
48940                 s[i].style.left = (pos-this.splitOffset) + "px";
48941             }
48942         }
48943     },
48944
48945     handleHiddenChange : function(colModel, colIndex, hidden){
48946         if(hidden){
48947             this.hideColumn(colIndex);
48948         }else{
48949             this.unhideColumn(colIndex);
48950         }
48951     },
48952
48953     hideColumn : function(colIndex){
48954         var cid = this.getColumnId(colIndex);
48955         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48956         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48957         if(Roo.isSafari){
48958             this.updateHeaders();
48959         }
48960         this.updateSplitters();
48961         this.layout();
48962     },
48963
48964     unhideColumn : function(colIndex){
48965         var cid = this.getColumnId(colIndex);
48966         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48967         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48968
48969         if(Roo.isSafari){
48970             this.updateHeaders();
48971         }
48972         this.updateSplitters();
48973         this.layout();
48974     },
48975
48976     insertRows : function(dm, firstRow, lastRow, isUpdate){
48977         if(firstRow == 0 && lastRow == dm.getCount()-1){
48978             this.refresh();
48979         }else{
48980             if(!isUpdate){
48981                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48982             }
48983             var s = this.getScrollState();
48984             var markup = this.renderRows(firstRow, lastRow);
48985             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48986             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48987             this.restoreScroll(s);
48988             if(!isUpdate){
48989                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48990                 this.syncRowHeights(firstRow, lastRow);
48991                 this.stripeRows(firstRow);
48992                 this.layout();
48993             }
48994         }
48995     },
48996
48997     bufferRows : function(markup, target, index){
48998         var before = null, trows = target.rows, tbody = target.tBodies[0];
48999         if(index < trows.length){
49000             before = trows[index];
49001         }
49002         var b = document.createElement("div");
49003         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49004         var rows = b.firstChild.rows;
49005         for(var i = 0, len = rows.length; i < len; i++){
49006             if(before){
49007                 tbody.insertBefore(rows[0], before);
49008             }else{
49009                 tbody.appendChild(rows[0]);
49010             }
49011         }
49012         b.innerHTML = "";
49013         b = null;
49014     },
49015
49016     deleteRows : function(dm, firstRow, lastRow){
49017         if(dm.getRowCount()<1){
49018             this.fireEvent("beforerefresh", this);
49019             this.mainBody.update("");
49020             this.lockedBody.update("");
49021             this.fireEvent("refresh", this);
49022         }else{
49023             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49024             var bt = this.getBodyTable();
49025             var tbody = bt.firstChild;
49026             var rows = bt.rows;
49027             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49028                 tbody.removeChild(rows[firstRow]);
49029             }
49030             this.stripeRows(firstRow);
49031             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49032         }
49033     },
49034
49035     updateRows : function(dataSource, firstRow, lastRow){
49036         var s = this.getScrollState();
49037         this.refresh();
49038         this.restoreScroll(s);
49039     },
49040
49041     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49042         if(!noRefresh){
49043            this.refresh();
49044         }
49045         this.updateHeaderSortState();
49046     },
49047
49048     getScrollState : function(){
49049         
49050         var sb = this.scroller.dom;
49051         return {left: sb.scrollLeft, top: sb.scrollTop};
49052     },
49053
49054     stripeRows : function(startRow){
49055         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49056             return;
49057         }
49058         startRow = startRow || 0;
49059         var rows = this.getBodyTable().rows;
49060         var lrows = this.getLockedTable().rows;
49061         var cls = ' x-grid-row-alt ';
49062         for(var i = startRow, len = rows.length; i < len; i++){
49063             var row = rows[i], lrow = lrows[i];
49064             var isAlt = ((i+1) % 2 == 0);
49065             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49066             if(isAlt == hasAlt){
49067                 continue;
49068             }
49069             if(isAlt){
49070                 row.className += " x-grid-row-alt";
49071             }else{
49072                 row.className = row.className.replace("x-grid-row-alt", "");
49073             }
49074             if(lrow){
49075                 lrow.className = row.className;
49076             }
49077         }
49078     },
49079
49080     restoreScroll : function(state){
49081         //Roo.log('GridView.restoreScroll');
49082         var sb = this.scroller.dom;
49083         sb.scrollLeft = state.left;
49084         sb.scrollTop = state.top;
49085         this.syncScroll();
49086     },
49087
49088     syncScroll : function(){
49089         //Roo.log('GridView.syncScroll');
49090         var sb = this.scroller.dom;
49091         var sh = this.mainHd.dom;
49092         var bs = this.mainBody.dom;
49093         var lv = this.lockedBody.dom;
49094         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49095         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49096     },
49097
49098     handleScroll : function(e){
49099         this.syncScroll();
49100         var sb = this.scroller.dom;
49101         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49102         e.stopEvent();
49103     },
49104
49105     handleWheel : function(e){
49106         var d = e.getWheelDelta();
49107         this.scroller.dom.scrollTop -= d*22;
49108         // set this here to prevent jumpy scrolling on large tables
49109         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49110         e.stopEvent();
49111     },
49112
49113     renderRows : function(startRow, endRow){
49114         // pull in all the crap needed to render rows
49115         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49116         var colCount = cm.getColumnCount();
49117
49118         if(ds.getCount() < 1){
49119             return ["", ""];
49120         }
49121
49122         // build a map for all the columns
49123         var cs = [];
49124         for(var i = 0; i < colCount; i++){
49125             var name = cm.getDataIndex(i);
49126             cs[i] = {
49127                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49128                 renderer : cm.getRenderer(i),
49129                 id : cm.getColumnId(i),
49130                 locked : cm.isLocked(i)
49131             };
49132         }
49133
49134         startRow = startRow || 0;
49135         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49136
49137         // records to render
49138         var rs = ds.getRange(startRow, endRow);
49139
49140         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49141     },
49142
49143     // As much as I hate to duplicate code, this was branched because FireFox really hates
49144     // [].join("") on strings. The performance difference was substantial enough to
49145     // branch this function
49146     doRender : Roo.isGecko ?
49147             function(cs, rs, ds, startRow, colCount, stripe){
49148                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49149                 // buffers
49150                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49151                 
49152                 var hasListener = this.grid.hasListener('rowclass');
49153                 var rowcfg = {};
49154                 for(var j = 0, len = rs.length; j < len; j++){
49155                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49156                     for(var i = 0; i < colCount; i++){
49157                         c = cs[i];
49158                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49159                         p.id = c.id;
49160                         p.css = p.attr = "";
49161                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49162                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49163                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49164                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49165                         }
49166                         var markup = ct.apply(p);
49167                         if(!c.locked){
49168                             cb+= markup;
49169                         }else{
49170                             lcb+= markup;
49171                         }
49172                     }
49173                     var alt = [];
49174                     if(stripe && ((rowIndex+1) % 2 == 0)){
49175                         alt.push("x-grid-row-alt")
49176                     }
49177                     if(r.dirty){
49178                         alt.push(  " x-grid-dirty-row");
49179                     }
49180                     rp.cells = lcb;
49181                     if(this.getRowClass){
49182                         alt.push(this.getRowClass(r, rowIndex));
49183                     }
49184                     if (hasListener) {
49185                         rowcfg = {
49186                              
49187                             record: r,
49188                             rowIndex : rowIndex,
49189                             rowClass : ''
49190                         }
49191                         this.grid.fireEvent('rowclass', this, rowcfg);
49192                         alt.push(rowcfg.rowClass);
49193                     }
49194                     rp.alt = alt.join(" ");
49195                     lbuf+= rt.apply(rp);
49196                     rp.cells = cb;
49197                     buf+=  rt.apply(rp);
49198                 }
49199                 return [lbuf, buf];
49200             } :
49201             function(cs, rs, ds, startRow, colCount, stripe){
49202                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49203                 // buffers
49204                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49205                 var hasListener = this.grid.hasListener('rowclass');
49206                 var rowcfg = {};
49207                 for(var j = 0, len = rs.length; j < len; j++){
49208                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49209                     for(var i = 0; i < colCount; i++){
49210                         c = cs[i];
49211                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49212                         p.id = c.id;
49213                         p.css = p.attr = "";
49214                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49215                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49216                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49217                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49218                         }
49219                         var markup = ct.apply(p);
49220                         if(!c.locked){
49221                             cb[cb.length] = markup;
49222                         }else{
49223                             lcb[lcb.length] = markup;
49224                         }
49225                     }
49226                     var alt = [];
49227                     if(stripe && ((rowIndex+1) % 2 == 0)){
49228                         alt.push( "x-grid-row-alt");
49229                     }
49230                     if(r.dirty){
49231                         alt.push(" x-grid-dirty-row");
49232                     }
49233                     rp.cells = lcb;
49234                     if(this.getRowClass){
49235                         alt.push( this.getRowClass(r, rowIndex));
49236                     }
49237                     if (hasListener) {
49238                         rowcfg = {
49239                              
49240                             record: r,
49241                             rowIndex : rowIndex,
49242                             rowClass : ''
49243                         }
49244                         this.grid.fireEvent('rowclass', this, rowcfg);
49245                         alt.push(rowcfg.rowClass);
49246                     }
49247                     rp.alt = alt.join(" ");
49248                     rp.cells = lcb.join("");
49249                     lbuf[lbuf.length] = rt.apply(rp);
49250                     rp.cells = cb.join("");
49251                     buf[buf.length] =  rt.apply(rp);
49252                 }
49253                 return [lbuf.join(""), buf.join("")];
49254             },
49255
49256     renderBody : function(){
49257         var markup = this.renderRows();
49258         var bt = this.templates.body;
49259         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49260     },
49261
49262     /**
49263      * Refreshes the grid
49264      * @param {Boolean} headersToo
49265      */
49266     refresh : function(headersToo){
49267         this.fireEvent("beforerefresh", this);
49268         this.grid.stopEditing();
49269         var result = this.renderBody();
49270         this.lockedBody.update(result[0]);
49271         this.mainBody.update(result[1]);
49272         if(headersToo === true){
49273             this.updateHeaders();
49274             this.updateColumns();
49275             this.updateSplitters();
49276             this.updateHeaderSortState();
49277         }
49278         this.syncRowHeights();
49279         this.layout();
49280         this.fireEvent("refresh", this);
49281     },
49282
49283     handleColumnMove : function(cm, oldIndex, newIndex){
49284         this.indexMap = null;
49285         var s = this.getScrollState();
49286         this.refresh(true);
49287         this.restoreScroll(s);
49288         this.afterMove(newIndex);
49289     },
49290
49291     afterMove : function(colIndex){
49292         if(this.enableMoveAnim && Roo.enableFx){
49293             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49294         }
49295         // if multisort - fix sortOrder, and reload..
49296         if (this.grid.dataSource.multiSort) {
49297             // the we can call sort again..
49298             var dm = this.grid.dataSource;
49299             var cm = this.grid.colModel;
49300             var so = [];
49301             for(var i = 0; i < cm.config.length; i++ ) {
49302                 
49303                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49304                     continue; // dont' bother, it's not in sort list or being set.
49305                 }
49306                 
49307                 so.push(cm.config[i].dataIndex);
49308             };
49309             dm.sortOrder = so;
49310             dm.load(dm.lastOptions);
49311             
49312             
49313         }
49314         
49315     },
49316
49317     updateCell : function(dm, rowIndex, dataIndex){
49318         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49319         if(typeof colIndex == "undefined"){ // not present in grid
49320             return;
49321         }
49322         var cm = this.grid.colModel;
49323         var cell = this.getCell(rowIndex, colIndex);
49324         var cellText = this.getCellText(rowIndex, colIndex);
49325
49326         var p = {
49327             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49328             id : cm.getColumnId(colIndex),
49329             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49330         };
49331         var renderer = cm.getRenderer(colIndex);
49332         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49333         if(typeof val == "undefined" || val === "") val = "&#160;";
49334         cellText.innerHTML = val;
49335         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49336         this.syncRowHeights(rowIndex, rowIndex);
49337     },
49338
49339     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49340         var maxWidth = 0;
49341         if(this.grid.autoSizeHeaders){
49342             var h = this.getHeaderCellMeasure(colIndex);
49343             maxWidth = Math.max(maxWidth, h.scrollWidth);
49344         }
49345         var tb, index;
49346         if(this.cm.isLocked(colIndex)){
49347             tb = this.getLockedTable();
49348             index = colIndex;
49349         }else{
49350             tb = this.getBodyTable();
49351             index = colIndex - this.cm.getLockedCount();
49352         }
49353         if(tb && tb.rows){
49354             var rows = tb.rows;
49355             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49356             for(var i = 0; i < stopIndex; i++){
49357                 var cell = rows[i].childNodes[index].firstChild;
49358                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49359             }
49360         }
49361         return maxWidth + /*margin for error in IE*/ 5;
49362     },
49363     /**
49364      * Autofit a column to its content.
49365      * @param {Number} colIndex
49366      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49367      */
49368      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49369          if(this.cm.isHidden(colIndex)){
49370              return; // can't calc a hidden column
49371          }
49372         if(forceMinSize){
49373             var cid = this.cm.getColumnId(colIndex);
49374             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49375            if(this.grid.autoSizeHeaders){
49376                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49377            }
49378         }
49379         var newWidth = this.calcColumnWidth(colIndex);
49380         this.cm.setColumnWidth(colIndex,
49381             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49382         if(!suppressEvent){
49383             this.grid.fireEvent("columnresize", colIndex, newWidth);
49384         }
49385     },
49386
49387     /**
49388      * Autofits all columns to their content and then expands to fit any extra space in the grid
49389      */
49390      autoSizeColumns : function(){
49391         var cm = this.grid.colModel;
49392         var colCount = cm.getColumnCount();
49393         for(var i = 0; i < colCount; i++){
49394             this.autoSizeColumn(i, true, true);
49395         }
49396         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49397             this.fitColumns();
49398         }else{
49399             this.updateColumns();
49400             this.layout();
49401         }
49402     },
49403
49404     /**
49405      * Autofits all columns to the grid's width proportionate with their current size
49406      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49407      */
49408     fitColumns : function(reserveScrollSpace){
49409         var cm = this.grid.colModel;
49410         var colCount = cm.getColumnCount();
49411         var cols = [];
49412         var width = 0;
49413         var i, w;
49414         for (i = 0; i < colCount; i++){
49415             if(!cm.isHidden(i) && !cm.isFixed(i)){
49416                 w = cm.getColumnWidth(i);
49417                 cols.push(i);
49418                 cols.push(w);
49419                 width += w;
49420             }
49421         }
49422         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49423         if(reserveScrollSpace){
49424             avail -= 17;
49425         }
49426         var frac = (avail - cm.getTotalWidth())/width;
49427         while (cols.length){
49428             w = cols.pop();
49429             i = cols.pop();
49430             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49431         }
49432         this.updateColumns();
49433         this.layout();
49434     },
49435
49436     onRowSelect : function(rowIndex){
49437         var row = this.getRowComposite(rowIndex);
49438         row.addClass("x-grid-row-selected");
49439     },
49440
49441     onRowDeselect : function(rowIndex){
49442         var row = this.getRowComposite(rowIndex);
49443         row.removeClass("x-grid-row-selected");
49444     },
49445
49446     onCellSelect : function(row, col){
49447         var cell = this.getCell(row, col);
49448         if(cell){
49449             Roo.fly(cell).addClass("x-grid-cell-selected");
49450         }
49451     },
49452
49453     onCellDeselect : function(row, col){
49454         var cell = this.getCell(row, col);
49455         if(cell){
49456             Roo.fly(cell).removeClass("x-grid-cell-selected");
49457         }
49458     },
49459
49460     updateHeaderSortState : function(){
49461         
49462         // sort state can be single { field: xxx, direction : yyy}
49463         // or   { xxx=>ASC , yyy : DESC ..... }
49464         
49465         var mstate = {};
49466         if (!this.ds.multiSort) { 
49467             var state = this.ds.getSortState();
49468             if(!state){
49469                 return;
49470             }
49471             mstate[state.field] = state.direction;
49472             // FIXME... - this is not used here.. but might be elsewhere..
49473             this.sortState = state;
49474             
49475         } else {
49476             mstate = this.ds.sortToggle;
49477         }
49478         //remove existing sort classes..
49479         
49480         var sc = this.sortClasses;
49481         var hds = this.el.select(this.headerSelector).removeClass(sc);
49482         
49483         for(var f in mstate) {
49484         
49485             var sortColumn = this.cm.findColumnIndex(f);
49486             
49487             if(sortColumn != -1){
49488                 var sortDir = mstate[f];        
49489                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49490             }
49491         }
49492         
49493          
49494         
49495     },
49496
49497
49498     handleHeaderClick : function(g, index){
49499         if(this.headersDisabled){
49500             return;
49501         }
49502         var dm = g.dataSource, cm = g.colModel;
49503         if(!cm.isSortable(index)){
49504             return;
49505         }
49506         g.stopEditing();
49507         
49508         if (dm.multiSort) {
49509             // update the sortOrder
49510             var so = [];
49511             for(var i = 0; i < cm.config.length; i++ ) {
49512                 
49513                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49514                     continue; // dont' bother, it's not in sort list or being set.
49515                 }
49516                 
49517                 so.push(cm.config[i].dataIndex);
49518             };
49519             dm.sortOrder = so;
49520         }
49521         
49522         
49523         dm.sort(cm.getDataIndex(index));
49524     },
49525
49526
49527     destroy : function(){
49528         if(this.colMenu){
49529             this.colMenu.removeAll();
49530             Roo.menu.MenuMgr.unregister(this.colMenu);
49531             this.colMenu.getEl().remove();
49532             delete this.colMenu;
49533         }
49534         if(this.hmenu){
49535             this.hmenu.removeAll();
49536             Roo.menu.MenuMgr.unregister(this.hmenu);
49537             this.hmenu.getEl().remove();
49538             delete this.hmenu;
49539         }
49540         if(this.grid.enableColumnMove){
49541             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49542             if(dds){
49543                 for(var dd in dds){
49544                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49545                         var elid = dds[dd].dragElId;
49546                         dds[dd].unreg();
49547                         Roo.get(elid).remove();
49548                     } else if(dds[dd].config.isTarget){
49549                         dds[dd].proxyTop.remove();
49550                         dds[dd].proxyBottom.remove();
49551                         dds[dd].unreg();
49552                     }
49553                     if(Roo.dd.DDM.locationCache[dd]){
49554                         delete Roo.dd.DDM.locationCache[dd];
49555                     }
49556                 }
49557                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49558             }
49559         }
49560         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49561         this.bind(null, null);
49562         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49563     },
49564
49565     handleLockChange : function(){
49566         this.refresh(true);
49567     },
49568
49569     onDenyColumnLock : function(){
49570
49571     },
49572
49573     onDenyColumnHide : function(){
49574
49575     },
49576
49577     handleHdMenuClick : function(item){
49578         var index = this.hdCtxIndex;
49579         var cm = this.cm, ds = this.ds;
49580         switch(item.id){
49581             case "asc":
49582                 ds.sort(cm.getDataIndex(index), "ASC");
49583                 break;
49584             case "desc":
49585                 ds.sort(cm.getDataIndex(index), "DESC");
49586                 break;
49587             case "lock":
49588                 var lc = cm.getLockedCount();
49589                 if(cm.getColumnCount(true) <= lc+1){
49590                     this.onDenyColumnLock();
49591                     return;
49592                 }
49593                 if(lc != index){
49594                     cm.setLocked(index, true, true);
49595                     cm.moveColumn(index, lc);
49596                     this.grid.fireEvent("columnmove", index, lc);
49597                 }else{
49598                     cm.setLocked(index, true);
49599                 }
49600             break;
49601             case "unlock":
49602                 var lc = cm.getLockedCount();
49603                 if((lc-1) != index){
49604                     cm.setLocked(index, false, true);
49605                     cm.moveColumn(index, lc-1);
49606                     this.grid.fireEvent("columnmove", index, lc-1);
49607                 }else{
49608                     cm.setLocked(index, false);
49609                 }
49610             break;
49611             default:
49612                 index = cm.getIndexById(item.id.substr(4));
49613                 if(index != -1){
49614                     if(item.checked && cm.getColumnCount(true) <= 1){
49615                         this.onDenyColumnHide();
49616                         return false;
49617                     }
49618                     cm.setHidden(index, item.checked);
49619                 }
49620         }
49621         return true;
49622     },
49623
49624     beforeColMenuShow : function(){
49625         var cm = this.cm,  colCount = cm.getColumnCount();
49626         this.colMenu.removeAll();
49627         for(var i = 0; i < colCount; i++){
49628             this.colMenu.add(new Roo.menu.CheckItem({
49629                 id: "col-"+cm.getColumnId(i),
49630                 text: cm.getColumnHeader(i),
49631                 checked: !cm.isHidden(i),
49632                 hideOnClick:false
49633             }));
49634         }
49635     },
49636
49637     handleHdCtx : function(g, index, e){
49638         e.stopEvent();
49639         var hd = this.getHeaderCell(index);
49640         this.hdCtxIndex = index;
49641         var ms = this.hmenu.items, cm = this.cm;
49642         ms.get("asc").setDisabled(!cm.isSortable(index));
49643         ms.get("desc").setDisabled(!cm.isSortable(index));
49644         if(this.grid.enableColLock !== false){
49645             ms.get("lock").setDisabled(cm.isLocked(index));
49646             ms.get("unlock").setDisabled(!cm.isLocked(index));
49647         }
49648         this.hmenu.show(hd, "tl-bl");
49649     },
49650
49651     handleHdOver : function(e){
49652         var hd = this.findHeaderCell(e.getTarget());
49653         if(hd && !this.headersDisabled){
49654             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49655                this.fly(hd).addClass("x-grid-hd-over");
49656             }
49657         }
49658     },
49659
49660     handleHdOut : function(e){
49661         var hd = this.findHeaderCell(e.getTarget());
49662         if(hd){
49663             this.fly(hd).removeClass("x-grid-hd-over");
49664         }
49665     },
49666
49667     handleSplitDblClick : function(e, t){
49668         var i = this.getCellIndex(t);
49669         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49670             this.autoSizeColumn(i, true);
49671             this.layout();
49672         }
49673     },
49674
49675     render : function(){
49676
49677         var cm = this.cm;
49678         var colCount = cm.getColumnCount();
49679
49680         if(this.grid.monitorWindowResize === true){
49681             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49682         }
49683         var header = this.renderHeaders();
49684         var body = this.templates.body.apply({rows:""});
49685         var html = this.templates.master.apply({
49686             lockedBody: body,
49687             body: body,
49688             lockedHeader: header[0],
49689             header: header[1]
49690         });
49691
49692         //this.updateColumns();
49693
49694         this.grid.getGridEl().dom.innerHTML = html;
49695
49696         this.initElements();
49697         
49698         // a kludge to fix the random scolling effect in webkit
49699         this.el.on("scroll", function() {
49700             this.el.dom.scrollTop=0; // hopefully not recursive..
49701         },this);
49702
49703         this.scroller.on("scroll", this.handleScroll, this);
49704         this.lockedBody.on("mousewheel", this.handleWheel, this);
49705         this.mainBody.on("mousewheel", this.handleWheel, this);
49706
49707         this.mainHd.on("mouseover", this.handleHdOver, this);
49708         this.mainHd.on("mouseout", this.handleHdOut, this);
49709         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49710                 {delegate: "."+this.splitClass});
49711
49712         this.lockedHd.on("mouseover", this.handleHdOver, this);
49713         this.lockedHd.on("mouseout", this.handleHdOut, this);
49714         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49715                 {delegate: "."+this.splitClass});
49716
49717         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49718             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49719         }
49720
49721         this.updateSplitters();
49722
49723         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49724             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49725             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49726         }
49727
49728         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49729             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49730             this.hmenu.add(
49731                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49732                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49733             );
49734             if(this.grid.enableColLock !== false){
49735                 this.hmenu.add('-',
49736                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49737                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49738                 );
49739             }
49740             if(this.grid.enableColumnHide !== false){
49741
49742                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49743                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49744                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49745
49746                 this.hmenu.add('-',
49747                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49748                 );
49749             }
49750             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49751
49752             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49753         }
49754
49755         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49756             this.dd = new Roo.grid.GridDragZone(this.grid, {
49757                 ddGroup : this.grid.ddGroup || 'GridDD'
49758             });
49759         }
49760
49761         /*
49762         for(var i = 0; i < colCount; i++){
49763             if(cm.isHidden(i)){
49764                 this.hideColumn(i);
49765             }
49766             if(cm.config[i].align){
49767                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49768                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49769             }
49770         }*/
49771         
49772         this.updateHeaderSortState();
49773
49774         this.beforeInitialResize();
49775         this.layout(true);
49776
49777         // two part rendering gives faster view to the user
49778         this.renderPhase2.defer(1, this);
49779     },
49780
49781     renderPhase2 : function(){
49782         // render the rows now
49783         this.refresh();
49784         if(this.grid.autoSizeColumns){
49785             this.autoSizeColumns();
49786         }
49787     },
49788
49789     beforeInitialResize : function(){
49790
49791     },
49792
49793     onColumnSplitterMoved : function(i, w){
49794         this.userResized = true;
49795         var cm = this.grid.colModel;
49796         cm.setColumnWidth(i, w, true);
49797         var cid = cm.getColumnId(i);
49798         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49799         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49800         this.updateSplitters();
49801         this.layout();
49802         this.grid.fireEvent("columnresize", i, w);
49803     },
49804
49805     syncRowHeights : function(startIndex, endIndex){
49806         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49807             startIndex = startIndex || 0;
49808             var mrows = this.getBodyTable().rows;
49809             var lrows = this.getLockedTable().rows;
49810             var len = mrows.length-1;
49811             endIndex = Math.min(endIndex || len, len);
49812             for(var i = startIndex; i <= endIndex; i++){
49813                 var m = mrows[i], l = lrows[i];
49814                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49815                 m.style.height = l.style.height = h + "px";
49816             }
49817         }
49818     },
49819
49820     layout : function(initialRender, is2ndPass){
49821         var g = this.grid;
49822         var auto = g.autoHeight;
49823         var scrollOffset = 16;
49824         var c = g.getGridEl(), cm = this.cm,
49825                 expandCol = g.autoExpandColumn,
49826                 gv = this;
49827         //c.beginMeasure();
49828
49829         if(!c.dom.offsetWidth){ // display:none?
49830             if(initialRender){
49831                 this.lockedWrap.show();
49832                 this.mainWrap.show();
49833             }
49834             return;
49835         }
49836
49837         var hasLock = this.cm.isLocked(0);
49838
49839         var tbh = this.headerPanel.getHeight();
49840         var bbh = this.footerPanel.getHeight();
49841
49842         if(auto){
49843             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49844             var newHeight = ch + c.getBorderWidth("tb");
49845             if(g.maxHeight){
49846                 newHeight = Math.min(g.maxHeight, newHeight);
49847             }
49848             c.setHeight(newHeight);
49849         }
49850
49851         if(g.autoWidth){
49852             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49853         }
49854
49855         var s = this.scroller;
49856
49857         var csize = c.getSize(true);
49858
49859         this.el.setSize(csize.width, csize.height);
49860
49861         this.headerPanel.setWidth(csize.width);
49862         this.footerPanel.setWidth(csize.width);
49863
49864         var hdHeight = this.mainHd.getHeight();
49865         var vw = csize.width;
49866         var vh = csize.height - (tbh + bbh);
49867
49868         s.setSize(vw, vh);
49869
49870         var bt = this.getBodyTable();
49871         var ltWidth = hasLock ?
49872                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49873
49874         var scrollHeight = bt.offsetHeight;
49875         var scrollWidth = ltWidth + bt.offsetWidth;
49876         var vscroll = false, hscroll = false;
49877
49878         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49879
49880         var lw = this.lockedWrap, mw = this.mainWrap;
49881         var lb = this.lockedBody, mb = this.mainBody;
49882
49883         setTimeout(function(){
49884             var t = s.dom.offsetTop;
49885             var w = s.dom.clientWidth,
49886                 h = s.dom.clientHeight;
49887
49888             lw.setTop(t);
49889             lw.setSize(ltWidth, h);
49890
49891             mw.setLeftTop(ltWidth, t);
49892             mw.setSize(w-ltWidth, h);
49893
49894             lb.setHeight(h-hdHeight);
49895             mb.setHeight(h-hdHeight);
49896
49897             if(is2ndPass !== true && !gv.userResized && expandCol){
49898                 // high speed resize without full column calculation
49899                 
49900                 var ci = cm.getIndexById(expandCol);
49901                 if (ci < 0) {
49902                     ci = cm.findColumnIndex(expandCol);
49903                 }
49904                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49905                 var expandId = cm.getColumnId(ci);
49906                 var  tw = cm.getTotalWidth(false);
49907                 var currentWidth = cm.getColumnWidth(ci);
49908                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49909                 if(currentWidth != cw){
49910                     cm.setColumnWidth(ci, cw, true);
49911                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49912                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49913                     gv.updateSplitters();
49914                     gv.layout(false, true);
49915                 }
49916             }
49917
49918             if(initialRender){
49919                 lw.show();
49920                 mw.show();
49921             }
49922             //c.endMeasure();
49923         }, 10);
49924     },
49925
49926     onWindowResize : function(){
49927         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49928             return;
49929         }
49930         this.layout();
49931     },
49932
49933     appendFooter : function(parentEl){
49934         return null;
49935     },
49936
49937     sortAscText : "Sort Ascending",
49938     sortDescText : "Sort Descending",
49939     lockText : "Lock Column",
49940     unlockText : "Unlock Column",
49941     columnsText : "Columns"
49942 });
49943
49944
49945 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49946     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49947     this.proxy.el.addClass('x-grid3-col-dd');
49948 };
49949
49950 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49951     handleMouseDown : function(e){
49952
49953     },
49954
49955     callHandleMouseDown : function(e){
49956         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49957     }
49958 });
49959 /*
49960  * Based on:
49961  * Ext JS Library 1.1.1
49962  * Copyright(c) 2006-2007, Ext JS, LLC.
49963  *
49964  * Originally Released Under LGPL - original licence link has changed is not relivant.
49965  *
49966  * Fork - LGPL
49967  * <script type="text/javascript">
49968  */
49969  
49970 // private
49971 // This is a support class used internally by the Grid components
49972 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49973     this.grid = grid;
49974     this.view = grid.getView();
49975     this.proxy = this.view.resizeProxy;
49976     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49977         "gridSplitters" + this.grid.getGridEl().id, {
49978         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49979     });
49980     this.setHandleElId(Roo.id(hd));
49981     this.setOuterHandleElId(Roo.id(hd2));
49982     this.scroll = false;
49983 };
49984 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49985     fly: Roo.Element.fly,
49986
49987     b4StartDrag : function(x, y){
49988         this.view.headersDisabled = true;
49989         this.proxy.setHeight(this.view.mainWrap.getHeight());
49990         var w = this.cm.getColumnWidth(this.cellIndex);
49991         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49992         this.resetConstraints();
49993         this.setXConstraint(minw, 1000);
49994         this.setYConstraint(0, 0);
49995         this.minX = x - minw;
49996         this.maxX = x + 1000;
49997         this.startPos = x;
49998         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49999     },
50000
50001
50002     handleMouseDown : function(e){
50003         ev = Roo.EventObject.setEvent(e);
50004         var t = this.fly(ev.getTarget());
50005         if(t.hasClass("x-grid-split")){
50006             this.cellIndex = this.view.getCellIndex(t.dom);
50007             this.split = t.dom;
50008             this.cm = this.grid.colModel;
50009             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50010                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50011             }
50012         }
50013     },
50014
50015     endDrag : function(e){
50016         this.view.headersDisabled = false;
50017         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50018         var diff = endX - this.startPos;
50019         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50020     },
50021
50022     autoOffset : function(){
50023         this.setDelta(0,0);
50024     }
50025 });/*
50026  * Based on:
50027  * Ext JS Library 1.1.1
50028  * Copyright(c) 2006-2007, Ext JS, LLC.
50029  *
50030  * Originally Released Under LGPL - original licence link has changed is not relivant.
50031  *
50032  * Fork - LGPL
50033  * <script type="text/javascript">
50034  */
50035  
50036 // private
50037 // This is a support class used internally by the Grid components
50038 Roo.grid.GridDragZone = function(grid, config){
50039     this.view = grid.getView();
50040     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50041     if(this.view.lockedBody){
50042         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50043         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50044     }
50045     this.scroll = false;
50046     this.grid = grid;
50047     this.ddel = document.createElement('div');
50048     this.ddel.className = 'x-grid-dd-wrap';
50049 };
50050
50051 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50052     ddGroup : "GridDD",
50053
50054     getDragData : function(e){
50055         var t = Roo.lib.Event.getTarget(e);
50056         var rowIndex = this.view.findRowIndex(t);
50057         if(rowIndex !== false){
50058             var sm = this.grid.selModel;
50059             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50060               //  sm.mouseDown(e, t);
50061             //}
50062             if (e.hasModifier()){
50063                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50064             }
50065             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50066         }
50067         return false;
50068     },
50069
50070     onInitDrag : function(e){
50071         var data = this.dragData;
50072         this.ddel.innerHTML = this.grid.getDragDropText();
50073         this.proxy.update(this.ddel);
50074         // fire start drag?
50075     },
50076
50077     afterRepair : function(){
50078         this.dragging = false;
50079     },
50080
50081     getRepairXY : function(e, data){
50082         return false;
50083     },
50084
50085     onEndDrag : function(data, e){
50086         // fire end drag?
50087     },
50088
50089     onValidDrop : function(dd, e, id){
50090         // fire drag drop?
50091         this.hideProxy();
50092     },
50093
50094     beforeInvalidDrop : function(e, id){
50095
50096     }
50097 });/*
50098  * Based on:
50099  * Ext JS Library 1.1.1
50100  * Copyright(c) 2006-2007, Ext JS, LLC.
50101  *
50102  * Originally Released Under LGPL - original licence link has changed is not relivant.
50103  *
50104  * Fork - LGPL
50105  * <script type="text/javascript">
50106  */
50107  
50108
50109 /**
50110  * @class Roo.grid.ColumnModel
50111  * @extends Roo.util.Observable
50112  * This is the default implementation of a ColumnModel used by the Grid. It defines
50113  * the columns in the grid.
50114  * <br>Usage:<br>
50115  <pre><code>
50116  var colModel = new Roo.grid.ColumnModel([
50117         {header: "Ticker", width: 60, sortable: true, locked: true},
50118         {header: "Company Name", width: 150, sortable: true},
50119         {header: "Market Cap.", width: 100, sortable: true},
50120         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50121         {header: "Employees", width: 100, sortable: true, resizable: false}
50122  ]);
50123  </code></pre>
50124  * <p>
50125  
50126  * The config options listed for this class are options which may appear in each
50127  * individual column definition.
50128  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50129  * @constructor
50130  * @param {Object} config An Array of column config objects. See this class's
50131  * config objects for details.
50132 */
50133 Roo.grid.ColumnModel = function(config){
50134         /**
50135      * The config passed into the constructor
50136      */
50137     this.config = config;
50138     this.lookup = {};
50139
50140     // if no id, create one
50141     // if the column does not have a dataIndex mapping,
50142     // map it to the order it is in the config
50143     for(var i = 0, len = config.length; i < len; i++){
50144         var c = config[i];
50145         if(typeof c.dataIndex == "undefined"){
50146             c.dataIndex = i;
50147         }
50148         if(typeof c.renderer == "string"){
50149             c.renderer = Roo.util.Format[c.renderer];
50150         }
50151         if(typeof c.id == "undefined"){
50152             c.id = Roo.id();
50153         }
50154         if(c.editor && c.editor.xtype){
50155             c.editor  = Roo.factory(c.editor, Roo.grid);
50156         }
50157         if(c.editor && c.editor.isFormField){
50158             c.editor = new Roo.grid.GridEditor(c.editor);
50159         }
50160         this.lookup[c.id] = c;
50161     }
50162
50163     /**
50164      * The width of columns which have no width specified (defaults to 100)
50165      * @type Number
50166      */
50167     this.defaultWidth = 100;
50168
50169     /**
50170      * Default sortable of columns which have no sortable specified (defaults to false)
50171      * @type Boolean
50172      */
50173     this.defaultSortable = false;
50174
50175     this.addEvents({
50176         /**
50177              * @event widthchange
50178              * Fires when the width of a column changes.
50179              * @param {ColumnModel} this
50180              * @param {Number} columnIndex The column index
50181              * @param {Number} newWidth The new width
50182              */
50183             "widthchange": true,
50184         /**
50185              * @event headerchange
50186              * Fires when the text of a header changes.
50187              * @param {ColumnModel} this
50188              * @param {Number} columnIndex The column index
50189              * @param {Number} newText The new header text
50190              */
50191             "headerchange": true,
50192         /**
50193              * @event hiddenchange
50194              * Fires when a column is hidden or "unhidden".
50195              * @param {ColumnModel} this
50196              * @param {Number} columnIndex The column index
50197              * @param {Boolean} hidden true if hidden, false otherwise
50198              */
50199             "hiddenchange": true,
50200             /**
50201          * @event columnmoved
50202          * Fires when a column is moved.
50203          * @param {ColumnModel} this
50204          * @param {Number} oldIndex
50205          * @param {Number} newIndex
50206          */
50207         "columnmoved" : true,
50208         /**
50209          * @event columlockchange
50210          * Fires when a column's locked state is changed
50211          * @param {ColumnModel} this
50212          * @param {Number} colIndex
50213          * @param {Boolean} locked true if locked
50214          */
50215         "columnlockchange" : true
50216     });
50217     Roo.grid.ColumnModel.superclass.constructor.call(this);
50218 };
50219 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50220     /**
50221      * @cfg {String} header The header text to display in the Grid view.
50222      */
50223     /**
50224      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50225      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50226      * specified, the column's index is used as an index into the Record's data Array.
50227      */
50228     /**
50229      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50230      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50231      */
50232     /**
50233      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50234      * Defaults to the value of the {@link #defaultSortable} property.
50235      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50236      */
50237     /**
50238      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50239      */
50240     /**
50241      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50242      */
50243     /**
50244      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50245      */
50246     /**
50247      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50248      */
50249     /**
50250      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50251      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50252      * default renderer uses the raw data value.
50253      */
50254        /**
50255      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50256      */
50257     /**
50258      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50259      */
50260
50261     /**
50262      * Returns the id of the column at the specified index.
50263      * @param {Number} index The column index
50264      * @return {String} the id
50265      */
50266     getColumnId : function(index){
50267         return this.config[index].id;
50268     },
50269
50270     /**
50271      * Returns the column for a specified id.
50272      * @param {String} id The column id
50273      * @return {Object} the column
50274      */
50275     getColumnById : function(id){
50276         return this.lookup[id];
50277     },
50278
50279     
50280     /**
50281      * Returns the column for a specified dataIndex.
50282      * @param {String} dataIndex The column dataIndex
50283      * @return {Object|Boolean} the column or false if not found
50284      */
50285     getColumnByDataIndex: function(dataIndex){
50286         var index = this.findColumnIndex(dataIndex);
50287         return index > -1 ? this.config[index] : false;
50288     },
50289     
50290     /**
50291      * Returns the index for a specified column id.
50292      * @param {String} id The column id
50293      * @return {Number} the index, or -1 if not found
50294      */
50295     getIndexById : function(id){
50296         for(var i = 0, len = this.config.length; i < len; i++){
50297             if(this.config[i].id == id){
50298                 return i;
50299             }
50300         }
50301         return -1;
50302     },
50303     
50304     /**
50305      * Returns the index for a specified column dataIndex.
50306      * @param {String} dataIndex The column dataIndex
50307      * @return {Number} the index, or -1 if not found
50308      */
50309     
50310     findColumnIndex : function(dataIndex){
50311         for(var i = 0, len = this.config.length; i < len; i++){
50312             if(this.config[i].dataIndex == dataIndex){
50313                 return i;
50314             }
50315         }
50316         return -1;
50317     },
50318     
50319     
50320     moveColumn : function(oldIndex, newIndex){
50321         var c = this.config[oldIndex];
50322         this.config.splice(oldIndex, 1);
50323         this.config.splice(newIndex, 0, c);
50324         this.dataMap = null;
50325         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50326     },
50327
50328     isLocked : function(colIndex){
50329         return this.config[colIndex].locked === true;
50330     },
50331
50332     setLocked : function(colIndex, value, suppressEvent){
50333         if(this.isLocked(colIndex) == value){
50334             return;
50335         }
50336         this.config[colIndex].locked = value;
50337         if(!suppressEvent){
50338             this.fireEvent("columnlockchange", this, colIndex, value);
50339         }
50340     },
50341
50342     getTotalLockedWidth : function(){
50343         var totalWidth = 0;
50344         for(var i = 0; i < this.config.length; i++){
50345             if(this.isLocked(i) && !this.isHidden(i)){
50346                 this.totalWidth += this.getColumnWidth(i);
50347             }
50348         }
50349         return totalWidth;
50350     },
50351
50352     getLockedCount : function(){
50353         for(var i = 0, len = this.config.length; i < len; i++){
50354             if(!this.isLocked(i)){
50355                 return i;
50356             }
50357         }
50358     },
50359
50360     /**
50361      * Returns the number of columns.
50362      * @return {Number}
50363      */
50364     getColumnCount : function(visibleOnly){
50365         if(visibleOnly === true){
50366             var c = 0;
50367             for(var i = 0, len = this.config.length; i < len; i++){
50368                 if(!this.isHidden(i)){
50369                     c++;
50370                 }
50371             }
50372             return c;
50373         }
50374         return this.config.length;
50375     },
50376
50377     /**
50378      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50379      * @param {Function} fn
50380      * @param {Object} scope (optional)
50381      * @return {Array} result
50382      */
50383     getColumnsBy : function(fn, scope){
50384         var r = [];
50385         for(var i = 0, len = this.config.length; i < len; i++){
50386             var c = this.config[i];
50387             if(fn.call(scope||this, c, i) === true){
50388                 r[r.length] = c;
50389             }
50390         }
50391         return r;
50392     },
50393
50394     /**
50395      * Returns true if the specified column is sortable.
50396      * @param {Number} col The column index
50397      * @return {Boolean}
50398      */
50399     isSortable : function(col){
50400         if(typeof this.config[col].sortable == "undefined"){
50401             return this.defaultSortable;
50402         }
50403         return this.config[col].sortable;
50404     },
50405
50406     /**
50407      * Returns the rendering (formatting) function defined for the column.
50408      * @param {Number} col The column index.
50409      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50410      */
50411     getRenderer : function(col){
50412         if(!this.config[col].renderer){
50413             return Roo.grid.ColumnModel.defaultRenderer;
50414         }
50415         return this.config[col].renderer;
50416     },
50417
50418     /**
50419      * Sets the rendering (formatting) function for a column.
50420      * @param {Number} col The column index
50421      * @param {Function} fn The function to use to process the cell's raw data
50422      * to return HTML markup for the grid view. The render function is called with
50423      * the following parameters:<ul>
50424      * <li>Data value.</li>
50425      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50426      * <li>css A CSS style string to apply to the table cell.</li>
50427      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50428      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50429      * <li>Row index</li>
50430      * <li>Column index</li>
50431      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50432      */
50433     setRenderer : function(col, fn){
50434         this.config[col].renderer = fn;
50435     },
50436
50437     /**
50438      * Returns the width for the specified column.
50439      * @param {Number} col The column index
50440      * @return {Number}
50441      */
50442     getColumnWidth : function(col){
50443         return this.config[col].width * 1 || this.defaultWidth;
50444     },
50445
50446     /**
50447      * Sets the width for a column.
50448      * @param {Number} col The column index
50449      * @param {Number} width The new width
50450      */
50451     setColumnWidth : function(col, width, suppressEvent){
50452         this.config[col].width = width;
50453         this.totalWidth = null;
50454         if(!suppressEvent){
50455              this.fireEvent("widthchange", this, col, width);
50456         }
50457     },
50458
50459     /**
50460      * Returns the total width of all columns.
50461      * @param {Boolean} includeHidden True to include hidden column widths
50462      * @return {Number}
50463      */
50464     getTotalWidth : function(includeHidden){
50465         if(!this.totalWidth){
50466             this.totalWidth = 0;
50467             for(var i = 0, len = this.config.length; i < len; i++){
50468                 if(includeHidden || !this.isHidden(i)){
50469                     this.totalWidth += this.getColumnWidth(i);
50470                 }
50471             }
50472         }
50473         return this.totalWidth;
50474     },
50475
50476     /**
50477      * Returns the header for the specified column.
50478      * @param {Number} col The column index
50479      * @return {String}
50480      */
50481     getColumnHeader : function(col){
50482         return this.config[col].header;
50483     },
50484
50485     /**
50486      * Sets the header for a column.
50487      * @param {Number} col The column index
50488      * @param {String} header The new header
50489      */
50490     setColumnHeader : function(col, header){
50491         this.config[col].header = header;
50492         this.fireEvent("headerchange", this, col, header);
50493     },
50494
50495     /**
50496      * Returns the tooltip for the specified column.
50497      * @param {Number} col The column index
50498      * @return {String}
50499      */
50500     getColumnTooltip : function(col){
50501             return this.config[col].tooltip;
50502     },
50503     /**
50504      * Sets the tooltip for a column.
50505      * @param {Number} col The column index
50506      * @param {String} tooltip The new tooltip
50507      */
50508     setColumnTooltip : function(col, tooltip){
50509             this.config[col].tooltip = tooltip;
50510     },
50511
50512     /**
50513      * Returns the dataIndex for the specified column.
50514      * @param {Number} col The column index
50515      * @return {Number}
50516      */
50517     getDataIndex : function(col){
50518         return this.config[col].dataIndex;
50519     },
50520
50521     /**
50522      * Sets the dataIndex for a column.
50523      * @param {Number} col The column index
50524      * @param {Number} dataIndex The new dataIndex
50525      */
50526     setDataIndex : function(col, dataIndex){
50527         this.config[col].dataIndex = dataIndex;
50528     },
50529
50530     
50531     
50532     /**
50533      * Returns true if the cell is editable.
50534      * @param {Number} colIndex The column index
50535      * @param {Number} rowIndex The row index
50536      * @return {Boolean}
50537      */
50538     isCellEditable : function(colIndex, rowIndex){
50539         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50540     },
50541
50542     /**
50543      * Returns the editor defined for the cell/column.
50544      * return false or null to disable editing.
50545      * @param {Number} colIndex The column index
50546      * @param {Number} rowIndex The row index
50547      * @return {Object}
50548      */
50549     getCellEditor : function(colIndex, rowIndex){
50550         return this.config[colIndex].editor;
50551     },
50552
50553     /**
50554      * Sets if a column is editable.
50555      * @param {Number} col The column index
50556      * @param {Boolean} editable True if the column is editable
50557      */
50558     setEditable : function(col, editable){
50559         this.config[col].editable = editable;
50560     },
50561
50562
50563     /**
50564      * Returns true if the column is hidden.
50565      * @param {Number} colIndex The column index
50566      * @return {Boolean}
50567      */
50568     isHidden : function(colIndex){
50569         return this.config[colIndex].hidden;
50570     },
50571
50572
50573     /**
50574      * Returns true if the column width cannot be changed
50575      */
50576     isFixed : function(colIndex){
50577         return this.config[colIndex].fixed;
50578     },
50579
50580     /**
50581      * Returns true if the column can be resized
50582      * @return {Boolean}
50583      */
50584     isResizable : function(colIndex){
50585         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50586     },
50587     /**
50588      * Sets if a column is hidden.
50589      * @param {Number} colIndex The column index
50590      * @param {Boolean} hidden True if the column is hidden
50591      */
50592     setHidden : function(colIndex, hidden){
50593         this.config[colIndex].hidden = hidden;
50594         this.totalWidth = null;
50595         this.fireEvent("hiddenchange", this, colIndex, hidden);
50596     },
50597
50598     /**
50599      * Sets the editor for a column.
50600      * @param {Number} col The column index
50601      * @param {Object} editor The editor object
50602      */
50603     setEditor : function(col, editor){
50604         this.config[col].editor = editor;
50605     }
50606 });
50607
50608 Roo.grid.ColumnModel.defaultRenderer = function(value){
50609         if(typeof value == "string" && value.length < 1){
50610             return "&#160;";
50611         }
50612         return value;
50613 };
50614
50615 // Alias for backwards compatibility
50616 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50617 /*
50618  * Based on:
50619  * Ext JS Library 1.1.1
50620  * Copyright(c) 2006-2007, Ext JS, LLC.
50621  *
50622  * Originally Released Under LGPL - original licence link has changed is not relivant.
50623  *
50624  * Fork - LGPL
50625  * <script type="text/javascript">
50626  */
50627
50628 /**
50629  * @class Roo.grid.AbstractSelectionModel
50630  * @extends Roo.util.Observable
50631  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50632  * implemented by descendant classes.  This class should not be directly instantiated.
50633  * @constructor
50634  */
50635 Roo.grid.AbstractSelectionModel = function(){
50636     this.locked = false;
50637     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50638 };
50639
50640 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50641     /** @ignore Called by the grid automatically. Do not call directly. */
50642     init : function(grid){
50643         this.grid = grid;
50644         this.initEvents();
50645     },
50646
50647     /**
50648      * Locks the selections.
50649      */
50650     lock : function(){
50651         this.locked = true;
50652     },
50653
50654     /**
50655      * Unlocks the selections.
50656      */
50657     unlock : function(){
50658         this.locked = false;
50659     },
50660
50661     /**
50662      * Returns true if the selections are locked.
50663      * @return {Boolean}
50664      */
50665     isLocked : function(){
50666         return this.locked;
50667     }
50668 });/*
50669  * Based on:
50670  * Ext JS Library 1.1.1
50671  * Copyright(c) 2006-2007, Ext JS, LLC.
50672  *
50673  * Originally Released Under LGPL - original licence link has changed is not relivant.
50674  *
50675  * Fork - LGPL
50676  * <script type="text/javascript">
50677  */
50678 /**
50679  * @extends Roo.grid.AbstractSelectionModel
50680  * @class Roo.grid.RowSelectionModel
50681  * The default SelectionModel used by {@link Roo.grid.Grid}.
50682  * It supports multiple selections and keyboard selection/navigation. 
50683  * @constructor
50684  * @param {Object} config
50685  */
50686 Roo.grid.RowSelectionModel = function(config){
50687     Roo.apply(this, config);
50688     this.selections = new Roo.util.MixedCollection(false, function(o){
50689         return o.id;
50690     });
50691
50692     this.last = false;
50693     this.lastActive = false;
50694
50695     this.addEvents({
50696         /**
50697              * @event selectionchange
50698              * Fires when the selection changes
50699              * @param {SelectionModel} this
50700              */
50701             "selectionchange" : true,
50702         /**
50703              * @event afterselectionchange
50704              * Fires after the selection changes (eg. by key press or clicking)
50705              * @param {SelectionModel} this
50706              */
50707             "afterselectionchange" : true,
50708         /**
50709              * @event beforerowselect
50710              * Fires when a row is selected being selected, return false to cancel.
50711              * @param {SelectionModel} this
50712              * @param {Number} rowIndex The selected index
50713              * @param {Boolean} keepExisting False if other selections will be cleared
50714              */
50715             "beforerowselect" : true,
50716         /**
50717              * @event rowselect
50718              * Fires when a row is selected.
50719              * @param {SelectionModel} this
50720              * @param {Number} rowIndex The selected index
50721              * @param {Roo.data.Record} r The record
50722              */
50723             "rowselect" : true,
50724         /**
50725              * @event rowdeselect
50726              * Fires when a row is deselected.
50727              * @param {SelectionModel} this
50728              * @param {Number} rowIndex The selected index
50729              */
50730         "rowdeselect" : true
50731     });
50732     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50733     this.locked = false;
50734 };
50735
50736 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50737     /**
50738      * @cfg {Boolean} singleSelect
50739      * True to allow selection of only one row at a time (defaults to false)
50740      */
50741     singleSelect : false,
50742
50743     // private
50744     initEvents : function(){
50745
50746         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50747             this.grid.on("mousedown", this.handleMouseDown, this);
50748         }else{ // allow click to work like normal
50749             this.grid.on("rowclick", this.handleDragableRowClick, this);
50750         }
50751
50752         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50753             "up" : function(e){
50754                 if(!e.shiftKey){
50755                     this.selectPrevious(e.shiftKey);
50756                 }else if(this.last !== false && this.lastActive !== false){
50757                     var last = this.last;
50758                     this.selectRange(this.last,  this.lastActive-1);
50759                     this.grid.getView().focusRow(this.lastActive);
50760                     if(last !== false){
50761                         this.last = last;
50762                     }
50763                 }else{
50764                     this.selectFirstRow();
50765                 }
50766                 this.fireEvent("afterselectionchange", this);
50767             },
50768             "down" : function(e){
50769                 if(!e.shiftKey){
50770                     this.selectNext(e.shiftKey);
50771                 }else if(this.last !== false && this.lastActive !== false){
50772                     var last = this.last;
50773                     this.selectRange(this.last,  this.lastActive+1);
50774                     this.grid.getView().focusRow(this.lastActive);
50775                     if(last !== false){
50776                         this.last = last;
50777                     }
50778                 }else{
50779                     this.selectFirstRow();
50780                 }
50781                 this.fireEvent("afterselectionchange", this);
50782             },
50783             scope: this
50784         });
50785
50786         var view = this.grid.view;
50787         view.on("refresh", this.onRefresh, this);
50788         view.on("rowupdated", this.onRowUpdated, this);
50789         view.on("rowremoved", this.onRemove, this);
50790     },
50791
50792     // private
50793     onRefresh : function(){
50794         var ds = this.grid.dataSource, i, v = this.grid.view;
50795         var s = this.selections;
50796         s.each(function(r){
50797             if((i = ds.indexOfId(r.id)) != -1){
50798                 v.onRowSelect(i);
50799             }else{
50800                 s.remove(r);
50801             }
50802         });
50803     },
50804
50805     // private
50806     onRemove : function(v, index, r){
50807         this.selections.remove(r);
50808     },
50809
50810     // private
50811     onRowUpdated : function(v, index, r){
50812         if(this.isSelected(r)){
50813             v.onRowSelect(index);
50814         }
50815     },
50816
50817     /**
50818      * Select records.
50819      * @param {Array} records The records to select
50820      * @param {Boolean} keepExisting (optional) True to keep existing selections
50821      */
50822     selectRecords : function(records, keepExisting){
50823         if(!keepExisting){
50824             this.clearSelections();
50825         }
50826         var ds = this.grid.dataSource;
50827         for(var i = 0, len = records.length; i < len; i++){
50828             this.selectRow(ds.indexOf(records[i]), true);
50829         }
50830     },
50831
50832     /**
50833      * Gets the number of selected rows.
50834      * @return {Number}
50835      */
50836     getCount : function(){
50837         return this.selections.length;
50838     },
50839
50840     /**
50841      * Selects the first row in the grid.
50842      */
50843     selectFirstRow : function(){
50844         this.selectRow(0);
50845     },
50846
50847     /**
50848      * Select the last row.
50849      * @param {Boolean} keepExisting (optional) True to keep existing selections
50850      */
50851     selectLastRow : function(keepExisting){
50852         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50853     },
50854
50855     /**
50856      * Selects the row immediately following the last selected row.
50857      * @param {Boolean} keepExisting (optional) True to keep existing selections
50858      */
50859     selectNext : function(keepExisting){
50860         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50861             this.selectRow(this.last+1, keepExisting);
50862             this.grid.getView().focusRow(this.last);
50863         }
50864     },
50865
50866     /**
50867      * Selects the row that precedes the last selected row.
50868      * @param {Boolean} keepExisting (optional) True to keep existing selections
50869      */
50870     selectPrevious : function(keepExisting){
50871         if(this.last){
50872             this.selectRow(this.last-1, keepExisting);
50873             this.grid.getView().focusRow(this.last);
50874         }
50875     },
50876
50877     /**
50878      * Returns the selected records
50879      * @return {Array} Array of selected records
50880      */
50881     getSelections : function(){
50882         return [].concat(this.selections.items);
50883     },
50884
50885     /**
50886      * Returns the first selected record.
50887      * @return {Record}
50888      */
50889     getSelected : function(){
50890         return this.selections.itemAt(0);
50891     },
50892
50893
50894     /**
50895      * Clears all selections.
50896      */
50897     clearSelections : function(fast){
50898         if(this.locked) return;
50899         if(fast !== true){
50900             var ds = this.grid.dataSource;
50901             var s = this.selections;
50902             s.each(function(r){
50903                 this.deselectRow(ds.indexOfId(r.id));
50904             }, this);
50905             s.clear();
50906         }else{
50907             this.selections.clear();
50908         }
50909         this.last = false;
50910     },
50911
50912
50913     /**
50914      * Selects all rows.
50915      */
50916     selectAll : function(){
50917         if(this.locked) return;
50918         this.selections.clear();
50919         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50920             this.selectRow(i, true);
50921         }
50922     },
50923
50924     /**
50925      * Returns True if there is a selection.
50926      * @return {Boolean}
50927      */
50928     hasSelection : function(){
50929         return this.selections.length > 0;
50930     },
50931
50932     /**
50933      * Returns True if the specified row is selected.
50934      * @param {Number/Record} record The record or index of the record to check
50935      * @return {Boolean}
50936      */
50937     isSelected : function(index){
50938         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50939         return (r && this.selections.key(r.id) ? true : false);
50940     },
50941
50942     /**
50943      * Returns True if the specified record id is selected.
50944      * @param {String} id The id of record to check
50945      * @return {Boolean}
50946      */
50947     isIdSelected : function(id){
50948         return (this.selections.key(id) ? true : false);
50949     },
50950
50951     // private
50952     handleMouseDown : function(e, t){
50953         var view = this.grid.getView(), rowIndex;
50954         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50955             return;
50956         };
50957         if(e.shiftKey && this.last !== false){
50958             var last = this.last;
50959             this.selectRange(last, rowIndex, e.ctrlKey);
50960             this.last = last; // reset the last
50961             view.focusRow(rowIndex);
50962         }else{
50963             var isSelected = this.isSelected(rowIndex);
50964             if(e.button !== 0 && isSelected){
50965                 view.focusRow(rowIndex);
50966             }else if(e.ctrlKey && isSelected){
50967                 this.deselectRow(rowIndex);
50968             }else if(!isSelected){
50969                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50970                 view.focusRow(rowIndex);
50971             }
50972         }
50973         this.fireEvent("afterselectionchange", this);
50974     },
50975     // private
50976     handleDragableRowClick :  function(grid, rowIndex, e) 
50977     {
50978         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50979             this.selectRow(rowIndex, false);
50980             grid.view.focusRow(rowIndex);
50981              this.fireEvent("afterselectionchange", this);
50982         }
50983     },
50984     
50985     /**
50986      * Selects multiple rows.
50987      * @param {Array} rows Array of the indexes of the row to select
50988      * @param {Boolean} keepExisting (optional) True to keep existing selections
50989      */
50990     selectRows : function(rows, keepExisting){
50991         if(!keepExisting){
50992             this.clearSelections();
50993         }
50994         for(var i = 0, len = rows.length; i < len; i++){
50995             this.selectRow(rows[i], true);
50996         }
50997     },
50998
50999     /**
51000      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51001      * @param {Number} startRow The index of the first row in the range
51002      * @param {Number} endRow The index of the last row in the range
51003      * @param {Boolean} keepExisting (optional) True to retain existing selections
51004      */
51005     selectRange : function(startRow, endRow, keepExisting){
51006         if(this.locked) return;
51007         if(!keepExisting){
51008             this.clearSelections();
51009         }
51010         if(startRow <= endRow){
51011             for(var i = startRow; i <= endRow; i++){
51012                 this.selectRow(i, true);
51013             }
51014         }else{
51015             for(var i = startRow; i >= endRow; i--){
51016                 this.selectRow(i, true);
51017             }
51018         }
51019     },
51020
51021     /**
51022      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51023      * @param {Number} startRow The index of the first row in the range
51024      * @param {Number} endRow The index of the last row in the range
51025      */
51026     deselectRange : function(startRow, endRow, preventViewNotify){
51027         if(this.locked) return;
51028         for(var i = startRow; i <= endRow; i++){
51029             this.deselectRow(i, preventViewNotify);
51030         }
51031     },
51032
51033     /**
51034      * Selects a row.
51035      * @param {Number} row The index of the row to select
51036      * @param {Boolean} keepExisting (optional) True to keep existing selections
51037      */
51038     selectRow : function(index, keepExisting, preventViewNotify){
51039         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51040         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51041             if(!keepExisting || this.singleSelect){
51042                 this.clearSelections();
51043             }
51044             var r = this.grid.dataSource.getAt(index);
51045             this.selections.add(r);
51046             this.last = this.lastActive = index;
51047             if(!preventViewNotify){
51048                 this.grid.getView().onRowSelect(index);
51049             }
51050             this.fireEvent("rowselect", this, index, r);
51051             this.fireEvent("selectionchange", this);
51052         }
51053     },
51054
51055     /**
51056      * Deselects a row.
51057      * @param {Number} row The index of the row to deselect
51058      */
51059     deselectRow : function(index, preventViewNotify){
51060         if(this.locked) return;
51061         if(this.last == index){
51062             this.last = false;
51063         }
51064         if(this.lastActive == index){
51065             this.lastActive = false;
51066         }
51067         var r = this.grid.dataSource.getAt(index);
51068         this.selections.remove(r);
51069         if(!preventViewNotify){
51070             this.grid.getView().onRowDeselect(index);
51071         }
51072         this.fireEvent("rowdeselect", this, index);
51073         this.fireEvent("selectionchange", this);
51074     },
51075
51076     // private
51077     restoreLast : function(){
51078         if(this._last){
51079             this.last = this._last;
51080         }
51081     },
51082
51083     // private
51084     acceptsNav : function(row, col, cm){
51085         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51086     },
51087
51088     // private
51089     onEditorKey : function(field, e){
51090         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51091         if(k == e.TAB){
51092             e.stopEvent();
51093             ed.completeEdit();
51094             if(e.shiftKey){
51095                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51096             }else{
51097                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51098             }
51099         }else if(k == e.ENTER && !e.ctrlKey){
51100             e.stopEvent();
51101             ed.completeEdit();
51102             if(e.shiftKey){
51103                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51104             }else{
51105                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51106             }
51107         }else if(k == e.ESC){
51108             ed.cancelEdit();
51109         }
51110         if(newCell){
51111             g.startEditing(newCell[0], newCell[1]);
51112         }
51113     }
51114 });/*
51115  * Based on:
51116  * Ext JS Library 1.1.1
51117  * Copyright(c) 2006-2007, Ext JS, LLC.
51118  *
51119  * Originally Released Under LGPL - original licence link has changed is not relivant.
51120  *
51121  * Fork - LGPL
51122  * <script type="text/javascript">
51123  */
51124 /**
51125  * @class Roo.grid.CellSelectionModel
51126  * @extends Roo.grid.AbstractSelectionModel
51127  * This class provides the basic implementation for cell selection in a grid.
51128  * @constructor
51129  * @param {Object} config The object containing the configuration of this model.
51130  */
51131 Roo.grid.CellSelectionModel = function(config){
51132     Roo.apply(this, config);
51133
51134     this.selection = null;
51135
51136     this.addEvents({
51137         /**
51138              * @event beforerowselect
51139              * Fires before a cell is selected.
51140              * @param {SelectionModel} this
51141              * @param {Number} rowIndex The selected row index
51142              * @param {Number} colIndex The selected cell index
51143              */
51144             "beforecellselect" : true,
51145         /**
51146              * @event cellselect
51147              * Fires when a cell is selected.
51148              * @param {SelectionModel} this
51149              * @param {Number} rowIndex The selected row index
51150              * @param {Number} colIndex The selected cell index
51151              */
51152             "cellselect" : true,
51153         /**
51154              * @event selectionchange
51155              * Fires when the active selection changes.
51156              * @param {SelectionModel} this
51157              * @param {Object} selection null for no selection or an object (o) with two properties
51158                 <ul>
51159                 <li>o.record: the record object for the row the selection is in</li>
51160                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51161                 </ul>
51162              */
51163             "selectionchange" : true
51164     });
51165     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51166 };
51167
51168 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51169
51170     /** @ignore */
51171     initEvents : function(){
51172         this.grid.on("mousedown", this.handleMouseDown, this);
51173         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51174         var view = this.grid.view;
51175         view.on("refresh", this.onViewChange, this);
51176         view.on("rowupdated", this.onRowUpdated, this);
51177         view.on("beforerowremoved", this.clearSelections, this);
51178         view.on("beforerowsinserted", this.clearSelections, this);
51179         if(this.grid.isEditor){
51180             this.grid.on("beforeedit", this.beforeEdit,  this);
51181         }
51182     },
51183
51184         //private
51185     beforeEdit : function(e){
51186         this.select(e.row, e.column, false, true, e.record);
51187     },
51188
51189         //private
51190     onRowUpdated : function(v, index, r){
51191         if(this.selection && this.selection.record == r){
51192             v.onCellSelect(index, this.selection.cell[1]);
51193         }
51194     },
51195
51196         //private
51197     onViewChange : function(){
51198         this.clearSelections(true);
51199     },
51200
51201         /**
51202          * Returns the currently selected cell,.
51203          * @return {Array} The selected cell (row, column) or null if none selected.
51204          */
51205     getSelectedCell : function(){
51206         return this.selection ? this.selection.cell : null;
51207     },
51208
51209     /**
51210      * Clears all selections.
51211      * @param {Boolean} true to prevent the gridview from being notified about the change.
51212      */
51213     clearSelections : function(preventNotify){
51214         var s = this.selection;
51215         if(s){
51216             if(preventNotify !== true){
51217                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51218             }
51219             this.selection = null;
51220             this.fireEvent("selectionchange", this, null);
51221         }
51222     },
51223
51224     /**
51225      * Returns true if there is a selection.
51226      * @return {Boolean}
51227      */
51228     hasSelection : function(){
51229         return this.selection ? true : false;
51230     },
51231
51232     /** @ignore */
51233     handleMouseDown : function(e, t){
51234         var v = this.grid.getView();
51235         if(this.isLocked()){
51236             return;
51237         };
51238         var row = v.findRowIndex(t);
51239         var cell = v.findCellIndex(t);
51240         if(row !== false && cell !== false){
51241             this.select(row, cell);
51242         }
51243     },
51244
51245     /**
51246      * Selects a cell.
51247      * @param {Number} rowIndex
51248      * @param {Number} collIndex
51249      */
51250     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51251         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51252             this.clearSelections();
51253             r = r || this.grid.dataSource.getAt(rowIndex);
51254             this.selection = {
51255                 record : r,
51256                 cell : [rowIndex, colIndex]
51257             };
51258             if(!preventViewNotify){
51259                 var v = this.grid.getView();
51260                 v.onCellSelect(rowIndex, colIndex);
51261                 if(preventFocus !== true){
51262                     v.focusCell(rowIndex, colIndex);
51263                 }
51264             }
51265             this.fireEvent("cellselect", this, rowIndex, colIndex);
51266             this.fireEvent("selectionchange", this, this.selection);
51267         }
51268     },
51269
51270         //private
51271     isSelectable : function(rowIndex, colIndex, cm){
51272         return !cm.isHidden(colIndex);
51273     },
51274
51275     /** @ignore */
51276     handleKeyDown : function(e){
51277         //Roo.log('Cell Sel Model handleKeyDown');
51278         if(!e.isNavKeyPress()){
51279             return;
51280         }
51281         var g = this.grid, s = this.selection;
51282         if(!s){
51283             e.stopEvent();
51284             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51285             if(cell){
51286                 this.select(cell[0], cell[1]);
51287             }
51288             return;
51289         }
51290         var sm = this;
51291         var walk = function(row, col, step){
51292             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51293         };
51294         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51295         var newCell;
51296
51297         switch(k){
51298             case e.TAB:
51299                 // handled by onEditorKey
51300                 if (g.isEditor && g.editing) {
51301                     return;
51302                 }
51303                 if(e.shiftKey){
51304                      newCell = walk(r, c-1, -1);
51305                 }else{
51306                      newCell = walk(r, c+1, 1);
51307                 }
51308              break;
51309              case e.DOWN:
51310                  newCell = walk(r+1, c, 1);
51311              break;
51312              case e.UP:
51313                  newCell = walk(r-1, c, -1);
51314              break;
51315              case e.RIGHT:
51316                  newCell = walk(r, c+1, 1);
51317              break;
51318              case e.LEFT:
51319                  newCell = walk(r, c-1, -1);
51320              break;
51321              case e.ENTER:
51322                  if(g.isEditor && !g.editing){
51323                     g.startEditing(r, c);
51324                     e.stopEvent();
51325                     return;
51326                 }
51327              break;
51328         };
51329         if(newCell){
51330             this.select(newCell[0], newCell[1]);
51331             e.stopEvent();
51332         }
51333     },
51334
51335     acceptsNav : function(row, col, cm){
51336         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51337     },
51338     /**
51339      * Selects a cell.
51340      * @param {Number} field (not used) - as it's normally used as a listener
51341      * @param {Number} e - event - fake it by using
51342      *
51343      * var e = Roo.EventObjectImpl.prototype;
51344      * e.keyCode = e.TAB
51345      *
51346      * 
51347      */
51348     onEditorKey : function(field, e){
51349         
51350         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51351         ///Roo.log('onEditorKey' + k);
51352         if (!ed) {
51353             
51354             
51355             
51356         }
51357         if(k == e.TAB){
51358             if(e.shiftKey){
51359                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51360             }else{
51361                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51362             }
51363             
51364             e.stopEvent();
51365             
51366         }else if(k == e.ENTER &&  !e.ctrlKey){
51367             ed.completeEdit();
51368             e.stopEvent();
51369             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51370         }else if(k == e.ESC){
51371             ed.cancelEdit();
51372         }
51373         
51374         
51375         if(newCell){
51376             //Roo.log('next cell after edit');
51377             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51378         }
51379     }
51380 });/*
51381  * Based on:
51382  * Ext JS Library 1.1.1
51383  * Copyright(c) 2006-2007, Ext JS, LLC.
51384  *
51385  * Originally Released Under LGPL - original licence link has changed is not relivant.
51386  *
51387  * Fork - LGPL
51388  * <script type="text/javascript">
51389  */
51390  
51391 /**
51392  * @class Roo.grid.EditorGrid
51393  * @extends Roo.grid.Grid
51394  * Class for creating and editable grid.
51395  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51396  * The container MUST have some type of size defined for the grid to fill. The container will be 
51397  * automatically set to position relative if it isn't already.
51398  * @param {Object} dataSource The data model to bind to
51399  * @param {Object} colModel The column model with info about this grid's columns
51400  */
51401 Roo.grid.EditorGrid = function(container, config){
51402     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51403     this.getGridEl().addClass("xedit-grid");
51404
51405     if(!this.selModel){
51406         this.selModel = new Roo.grid.CellSelectionModel();
51407     }
51408
51409     this.activeEditor = null;
51410
51411         this.addEvents({
51412             /**
51413              * @event beforeedit
51414              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51415              * <ul style="padding:5px;padding-left:16px;">
51416              * <li>grid - This grid</li>
51417              * <li>record - The record being edited</li>
51418              * <li>field - The field name being edited</li>
51419              * <li>value - The value for the field being edited.</li>
51420              * <li>row - The grid row index</li>
51421              * <li>column - The grid column index</li>
51422              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51423              * </ul>
51424              * @param {Object} e An edit event (see above for description)
51425              */
51426             "beforeedit" : true,
51427             /**
51428              * @event afteredit
51429              * Fires after a cell is edited. <br />
51430              * <ul style="padding:5px;padding-left:16px;">
51431              * <li>grid - This grid</li>
51432              * <li>record - The record being edited</li>
51433              * <li>field - The field name being edited</li>
51434              * <li>value - The value being set</li>
51435              * <li>originalValue - The original value for the field, before the edit.</li>
51436              * <li>row - The grid row index</li>
51437              * <li>column - The grid column index</li>
51438              * </ul>
51439              * @param {Object} e An edit event (see above for description)
51440              */
51441             "afteredit" : true,
51442             /**
51443              * @event validateedit
51444              * Fires after a cell is edited, but before the value is set in the record. 
51445          * You can use this to modify the value being set in the field, Return false
51446              * to cancel the change. The edit event object has the following properties <br />
51447              * <ul style="padding:5px;padding-left:16px;">
51448          * <li>editor - This editor</li>
51449              * <li>grid - This grid</li>
51450              * <li>record - The record being edited</li>
51451              * <li>field - The field name being edited</li>
51452              * <li>value - The value being set</li>
51453              * <li>originalValue - The original value for the field, before the edit.</li>
51454              * <li>row - The grid row index</li>
51455              * <li>column - The grid column index</li>
51456              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51457              * </ul>
51458              * @param {Object} e An edit event (see above for description)
51459              */
51460             "validateedit" : true
51461         });
51462     this.on("bodyscroll", this.stopEditing,  this);
51463     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51464 };
51465
51466 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51467     /**
51468      * @cfg {Number} clicksToEdit
51469      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51470      */
51471     clicksToEdit: 2,
51472
51473     // private
51474     isEditor : true,
51475     // private
51476     trackMouseOver: false, // causes very odd FF errors
51477
51478     onCellDblClick : function(g, row, col){
51479         this.startEditing(row, col);
51480     },
51481
51482     onEditComplete : function(ed, value, startValue){
51483         this.editing = false;
51484         this.activeEditor = null;
51485         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51486         var r = ed.record;
51487         var field = this.colModel.getDataIndex(ed.col);
51488         var e = {
51489             grid: this,
51490             record: r,
51491             field: field,
51492             originalValue: startValue,
51493             value: value,
51494             row: ed.row,
51495             column: ed.col,
51496             cancel:false,
51497             editor: ed
51498         };
51499         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51500         cell.show();
51501           
51502         if(String(value) !== String(startValue)){
51503             
51504             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51505                 r.set(field, e.value);
51506                 // if we are dealing with a combo box..
51507                 // then we also set the 'name' colum to be the displayField
51508                 if (ed.field.displayField && ed.field.name) {
51509                     r.set(ed.field.name, ed.field.el.dom.value);
51510                 }
51511                 
51512                 delete e.cancel; //?? why!!!
51513                 this.fireEvent("afteredit", e);
51514             }
51515         } else {
51516             this.fireEvent("afteredit", e); // always fire it!
51517         }
51518         this.view.focusCell(ed.row, ed.col);
51519     },
51520
51521     /**
51522      * Starts editing the specified for the specified row/column
51523      * @param {Number} rowIndex
51524      * @param {Number} colIndex
51525      */
51526     startEditing : function(row, col){
51527         this.stopEditing();
51528         if(this.colModel.isCellEditable(col, row)){
51529             this.view.ensureVisible(row, col, true);
51530           
51531             var r = this.dataSource.getAt(row);
51532             var field = this.colModel.getDataIndex(col);
51533             var cell = Roo.get(this.view.getCell(row,col));
51534             var e = {
51535                 grid: this,
51536                 record: r,
51537                 field: field,
51538                 value: r.data[field],
51539                 row: row,
51540                 column: col,
51541                 cancel:false 
51542             };
51543             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51544                 this.editing = true;
51545                 var ed = this.colModel.getCellEditor(col, row);
51546                 
51547                 if (!ed) {
51548                     return;
51549                 }
51550                 if(!ed.rendered){
51551                     ed.render(ed.parentEl || document.body);
51552                 }
51553                 ed.field.reset();
51554                
51555                 cell.hide();
51556                 
51557                 (function(){ // complex but required for focus issues in safari, ie and opera
51558                     ed.row = row;
51559                     ed.col = col;
51560                     ed.record = r;
51561                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51562                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51563                     this.activeEditor = ed;
51564                     var v = r.data[field];
51565                     ed.startEdit(this.view.getCell(row, col), v);
51566                     // combo's with 'displayField and name set
51567                     if (ed.field.displayField && ed.field.name) {
51568                         ed.field.el.dom.value = r.data[ed.field.name];
51569                     }
51570                     
51571                     
51572                 }).defer(50, this);
51573             }
51574         }
51575     },
51576         
51577     /**
51578      * Stops any active editing
51579      */
51580     stopEditing : function(){
51581         if(this.activeEditor){
51582             this.activeEditor.completeEdit();
51583         }
51584         this.activeEditor = null;
51585     }
51586 });/*
51587  * Based on:
51588  * Ext JS Library 1.1.1
51589  * Copyright(c) 2006-2007, Ext JS, LLC.
51590  *
51591  * Originally Released Under LGPL - original licence link has changed is not relivant.
51592  *
51593  * Fork - LGPL
51594  * <script type="text/javascript">
51595  */
51596
51597 // private - not really -- you end up using it !
51598 // This is a support class used internally by the Grid components
51599
51600 /**
51601  * @class Roo.grid.GridEditor
51602  * @extends Roo.Editor
51603  * Class for creating and editable grid elements.
51604  * @param {Object} config any settings (must include field)
51605  */
51606 Roo.grid.GridEditor = function(field, config){
51607     if (!config && field.field) {
51608         config = field;
51609         field = Roo.factory(config.field, Roo.form);
51610     }
51611     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51612     field.monitorTab = false;
51613 };
51614
51615 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51616     
51617     /**
51618      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51619      */
51620     
51621     alignment: "tl-tl",
51622     autoSize: "width",
51623     hideEl : false,
51624     cls: "x-small-editor x-grid-editor",
51625     shim:false,
51626     shadow:"frame"
51627 });/*
51628  * Based on:
51629  * Ext JS Library 1.1.1
51630  * Copyright(c) 2006-2007, Ext JS, LLC.
51631  *
51632  * Originally Released Under LGPL - original licence link has changed is not relivant.
51633  *
51634  * Fork - LGPL
51635  * <script type="text/javascript">
51636  */
51637   
51638
51639   
51640 Roo.grid.PropertyRecord = Roo.data.Record.create([
51641     {name:'name',type:'string'},  'value'
51642 ]);
51643
51644
51645 Roo.grid.PropertyStore = function(grid, source){
51646     this.grid = grid;
51647     this.store = new Roo.data.Store({
51648         recordType : Roo.grid.PropertyRecord
51649     });
51650     this.store.on('update', this.onUpdate,  this);
51651     if(source){
51652         this.setSource(source);
51653     }
51654     Roo.grid.PropertyStore.superclass.constructor.call(this);
51655 };
51656
51657
51658
51659 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51660     setSource : function(o){
51661         this.source = o;
51662         this.store.removeAll();
51663         var data = [];
51664         for(var k in o){
51665             if(this.isEditableValue(o[k])){
51666                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51667             }
51668         }
51669         this.store.loadRecords({records: data}, {}, true);
51670     },
51671
51672     onUpdate : function(ds, record, type){
51673         if(type == Roo.data.Record.EDIT){
51674             var v = record.data['value'];
51675             var oldValue = record.modified['value'];
51676             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51677                 this.source[record.id] = v;
51678                 record.commit();
51679                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51680             }else{
51681                 record.reject();
51682             }
51683         }
51684     },
51685
51686     getProperty : function(row){
51687        return this.store.getAt(row);
51688     },
51689
51690     isEditableValue: function(val){
51691         if(val && val instanceof Date){
51692             return true;
51693         }else if(typeof val == 'object' || typeof val == 'function'){
51694             return false;
51695         }
51696         return true;
51697     },
51698
51699     setValue : function(prop, value){
51700         this.source[prop] = value;
51701         this.store.getById(prop).set('value', value);
51702     },
51703
51704     getSource : function(){
51705         return this.source;
51706     }
51707 });
51708
51709 Roo.grid.PropertyColumnModel = function(grid, store){
51710     this.grid = grid;
51711     var g = Roo.grid;
51712     g.PropertyColumnModel.superclass.constructor.call(this, [
51713         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51714         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51715     ]);
51716     this.store = store;
51717     this.bselect = Roo.DomHelper.append(document.body, {
51718         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51719             {tag: 'option', value: 'true', html: 'true'},
51720             {tag: 'option', value: 'false', html: 'false'}
51721         ]
51722     });
51723     Roo.id(this.bselect);
51724     var f = Roo.form;
51725     this.editors = {
51726         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51727         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51728         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51729         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51730         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51731     };
51732     this.renderCellDelegate = this.renderCell.createDelegate(this);
51733     this.renderPropDelegate = this.renderProp.createDelegate(this);
51734 };
51735
51736 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51737     
51738     
51739     nameText : 'Name',
51740     valueText : 'Value',
51741     
51742     dateFormat : 'm/j/Y',
51743     
51744     
51745     renderDate : function(dateVal){
51746         return dateVal.dateFormat(this.dateFormat);
51747     },
51748
51749     renderBool : function(bVal){
51750         return bVal ? 'true' : 'false';
51751     },
51752
51753     isCellEditable : function(colIndex, rowIndex){
51754         return colIndex == 1;
51755     },
51756
51757     getRenderer : function(col){
51758         return col == 1 ?
51759             this.renderCellDelegate : this.renderPropDelegate;
51760     },
51761
51762     renderProp : function(v){
51763         return this.getPropertyName(v);
51764     },
51765
51766     renderCell : function(val){
51767         var rv = val;
51768         if(val instanceof Date){
51769             rv = this.renderDate(val);
51770         }else if(typeof val == 'boolean'){
51771             rv = this.renderBool(val);
51772         }
51773         return Roo.util.Format.htmlEncode(rv);
51774     },
51775
51776     getPropertyName : function(name){
51777         var pn = this.grid.propertyNames;
51778         return pn && pn[name] ? pn[name] : name;
51779     },
51780
51781     getCellEditor : function(colIndex, rowIndex){
51782         var p = this.store.getProperty(rowIndex);
51783         var n = p.data['name'], val = p.data['value'];
51784         
51785         if(typeof(this.grid.customEditors[n]) == 'string'){
51786             return this.editors[this.grid.customEditors[n]];
51787         }
51788         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51789             return this.grid.customEditors[n];
51790         }
51791         if(val instanceof Date){
51792             return this.editors['date'];
51793         }else if(typeof val == 'number'){
51794             return this.editors['number'];
51795         }else if(typeof val == 'boolean'){
51796             return this.editors['boolean'];
51797         }else{
51798             return this.editors['string'];
51799         }
51800     }
51801 });
51802
51803 /**
51804  * @class Roo.grid.PropertyGrid
51805  * @extends Roo.grid.EditorGrid
51806  * This class represents the  interface of a component based property grid control.
51807  * <br><br>Usage:<pre><code>
51808  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51809       
51810  });
51811  // set any options
51812  grid.render();
51813  * </code></pre>
51814   
51815  * @constructor
51816  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51817  * The container MUST have some type of size defined for the grid to fill. The container will be
51818  * automatically set to position relative if it isn't already.
51819  * @param {Object} config A config object that sets properties on this grid.
51820  */
51821 Roo.grid.PropertyGrid = function(container, config){
51822     config = config || {};
51823     var store = new Roo.grid.PropertyStore(this);
51824     this.store = store;
51825     var cm = new Roo.grid.PropertyColumnModel(this, store);
51826     store.store.sort('name', 'ASC');
51827     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51828         ds: store.store,
51829         cm: cm,
51830         enableColLock:false,
51831         enableColumnMove:false,
51832         stripeRows:false,
51833         trackMouseOver: false,
51834         clicksToEdit:1
51835     }, config));
51836     this.getGridEl().addClass('x-props-grid');
51837     this.lastEditRow = null;
51838     this.on('columnresize', this.onColumnResize, this);
51839     this.addEvents({
51840          /**
51841              * @event beforepropertychange
51842              * Fires before a property changes (return false to stop?)
51843              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51844              * @param {String} id Record Id
51845              * @param {String} newval New Value
51846          * @param {String} oldval Old Value
51847              */
51848         "beforepropertychange": true,
51849         /**
51850              * @event propertychange
51851              * Fires after a property changes
51852              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51853              * @param {String} id Record Id
51854              * @param {String} newval New Value
51855          * @param {String} oldval Old Value
51856              */
51857         "propertychange": true
51858     });
51859     this.customEditors = this.customEditors || {};
51860 };
51861 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51862     
51863      /**
51864      * @cfg {Object} customEditors map of colnames=> custom editors.
51865      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51866      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51867      * false disables editing of the field.
51868          */
51869     
51870       /**
51871      * @cfg {Object} propertyNames map of property Names to their displayed value
51872          */
51873     
51874     render : function(){
51875         Roo.grid.PropertyGrid.superclass.render.call(this);
51876         this.autoSize.defer(100, this);
51877     },
51878
51879     autoSize : function(){
51880         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51881         if(this.view){
51882             this.view.fitColumns();
51883         }
51884     },
51885
51886     onColumnResize : function(){
51887         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51888         this.autoSize();
51889     },
51890     /**
51891      * Sets the data for the Grid
51892      * accepts a Key => Value object of all the elements avaiable.
51893      * @param {Object} data  to appear in grid.
51894      */
51895     setSource : function(source){
51896         this.store.setSource(source);
51897         //this.autoSize();
51898     },
51899     /**
51900      * Gets all the data from the grid.
51901      * @return {Object} data  data stored in grid
51902      */
51903     getSource : function(){
51904         return this.store.getSource();
51905     }
51906 });/*
51907  * Based on:
51908  * Ext JS Library 1.1.1
51909  * Copyright(c) 2006-2007, Ext JS, LLC.
51910  *
51911  * Originally Released Under LGPL - original licence link has changed is not relivant.
51912  *
51913  * Fork - LGPL
51914  * <script type="text/javascript">
51915  */
51916  
51917 /**
51918  * @class Roo.LoadMask
51919  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51920  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51921  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51922  * element's UpdateManager load indicator and will be destroyed after the initial load.
51923  * @constructor
51924  * Create a new LoadMask
51925  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51926  * @param {Object} config The config object
51927  */
51928 Roo.LoadMask = function(el, config){
51929     this.el = Roo.get(el);
51930     Roo.apply(this, config);
51931     if(this.store){
51932         this.store.on('beforeload', this.onBeforeLoad, this);
51933         this.store.on('load', this.onLoad, this);
51934         this.store.on('loadexception', this.onLoad, this);
51935         this.removeMask = false;
51936     }else{
51937         var um = this.el.getUpdateManager();
51938         um.showLoadIndicator = false; // disable the default indicator
51939         um.on('beforeupdate', this.onBeforeLoad, this);
51940         um.on('update', this.onLoad, this);
51941         um.on('failure', this.onLoad, this);
51942         this.removeMask = true;
51943     }
51944 };
51945
51946 Roo.LoadMask.prototype = {
51947     /**
51948      * @cfg {Boolean} removeMask
51949      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51950      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51951      */
51952     /**
51953      * @cfg {String} msg
51954      * The text to display in a centered loading message box (defaults to 'Loading...')
51955      */
51956     msg : 'Loading...',
51957     /**
51958      * @cfg {String} msgCls
51959      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51960      */
51961     msgCls : 'x-mask-loading',
51962
51963     /**
51964      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51965      * @type Boolean
51966      */
51967     disabled: false,
51968
51969     /**
51970      * Disables the mask to prevent it from being displayed
51971      */
51972     disable : function(){
51973        this.disabled = true;
51974     },
51975
51976     /**
51977      * Enables the mask so that it can be displayed
51978      */
51979     enable : function(){
51980         this.disabled = false;
51981     },
51982
51983     // private
51984     onLoad : function(){
51985         this.el.unmask(this.removeMask);
51986     },
51987
51988     // private
51989     onBeforeLoad : function(){
51990         if(!this.disabled){
51991             this.el.mask(this.msg, this.msgCls);
51992         }
51993     },
51994
51995     // private
51996     destroy : function(){
51997         if(this.store){
51998             this.store.un('beforeload', this.onBeforeLoad, this);
51999             this.store.un('load', this.onLoad, this);
52000             this.store.un('loadexception', this.onLoad, this);
52001         }else{
52002             var um = this.el.getUpdateManager();
52003             um.un('beforeupdate', this.onBeforeLoad, this);
52004             um.un('update', this.onLoad, this);
52005             um.un('failure', this.onLoad, this);
52006         }
52007     }
52008 };/*
52009  * Based on:
52010  * Ext JS Library 1.1.1
52011  * Copyright(c) 2006-2007, Ext JS, LLC.
52012  *
52013  * Originally Released Under LGPL - original licence link has changed is not relivant.
52014  *
52015  * Fork - LGPL
52016  * <script type="text/javascript">
52017  */
52018 Roo.XTemplate = function(){
52019     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52020     var s = this.html;
52021
52022     s = ['<tpl>', s, '</tpl>'].join('');
52023
52024     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52025
52026     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52027     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52028     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52029     var m, id = 0;
52030     var tpls = [];
52031
52032     while(m = s.match(re)){
52033        var m2 = m[0].match(nameRe);
52034        var m3 = m[0].match(ifRe);
52035        var m4 = m[0].match(execRe);
52036        var exp = null, fn = null, exec = null;
52037        var name = m2 && m2[1] ? m2[1] : '';
52038        if(m3){
52039            exp = m3 && m3[1] ? m3[1] : null;
52040            if(exp){
52041                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52042            }
52043        }
52044        if(m4){
52045            exp = m4 && m4[1] ? m4[1] : null;
52046            if(exp){
52047                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52048            }
52049        }
52050        if(name){
52051            switch(name){
52052                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52053                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52054                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52055            }
52056        }
52057        tpls.push({
52058             id: id,
52059             target: name,
52060             exec: exec,
52061             test: fn,
52062             body: m[1]||''
52063         });
52064        s = s.replace(m[0], '{xtpl'+ id + '}');
52065        ++id;
52066     }
52067     for(var i = tpls.length-1; i >= 0; --i){
52068         this.compileTpl(tpls[i]);
52069     }
52070     this.master = tpls[tpls.length-1];
52071     this.tpls = tpls;
52072 };
52073 Roo.extend(Roo.XTemplate, Roo.Template, {
52074
52075     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52076
52077     applySubTemplate : function(id, values, parent){
52078         var t = this.tpls[id];
52079         if(t.test && !t.test.call(this, values, parent)){
52080             return '';
52081         }
52082         if(t.exec && t.exec.call(this, values, parent)){
52083             return '';
52084         }
52085         var vs = t.target ? t.target.call(this, values, parent) : values;
52086         parent = t.target ? values : parent;
52087         if(t.target && vs instanceof Array){
52088             var buf = [];
52089             for(var i = 0, len = vs.length; i < len; i++){
52090                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52091             }
52092             return buf.join('');
52093         }
52094         return t.compiled.call(this, vs, parent);
52095     },
52096
52097     compileTpl : function(tpl){
52098         var fm = Roo.util.Format;
52099         var useF = this.disableFormats !== true;
52100         var sep = Roo.isGecko ? "+" : ",";
52101         var fn = function(m, name, format, args){
52102             if(name.substr(0, 4) == 'xtpl'){
52103                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52104             }
52105             var v;
52106             if(name.indexOf('.') != -1){
52107                 v = name;
52108             }else{
52109                 v = "values['" + name + "']";
52110             }
52111             if(format && useF){
52112                 args = args ? ',' + args : "";
52113                 if(format.substr(0, 5) != "this."){
52114                     format = "fm." + format + '(';
52115                 }else{
52116                     format = 'this.call("'+ format.substr(5) + '", ';
52117                     args = ", values";
52118                 }
52119             }else{
52120                 args= ''; format = "("+v+" === undefined ? '' : ";
52121             }
52122             return "'"+ sep + format + v + args + ")"+sep+"'";
52123         };
52124         var body;
52125         // branched to use + in gecko and [].join() in others
52126         if(Roo.isGecko){
52127             body = "tpl.compiled = function(values, parent){ return '" +
52128                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52129                     "';};";
52130         }else{
52131             body = ["tpl.compiled = function(values, parent){ return ['"];
52132             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52133             body.push("'].join('');};");
52134             body = body.join('');
52135         }
52136         /** eval:var:zzzzzzz */
52137         eval(body);
52138         return this;
52139     },
52140
52141     applyTemplate : function(values){
52142         return this.master.compiled.call(this, values, {});
52143         var s = this.subs;
52144     },
52145
52146     apply : function(){
52147         return this.applyTemplate.apply(this, arguments);
52148     },
52149
52150     compile : function(){return this;}
52151 });
52152
52153 Roo.XTemplate.from = function(el){
52154     el = Roo.getDom(el);
52155     return new Roo.XTemplate(el.value || el.innerHTML);
52156 };/*
52157  * Original code for Roojs - LGPL
52158  * <script type="text/javascript">
52159  */
52160  
52161 /**
52162  * @class Roo.XComponent
52163  * A delayed Element creator...
52164  * Or a way to group chunks of interface together.
52165  * 
52166  * Mypart.xyx = new Roo.XComponent({
52167
52168     parent : 'Mypart.xyz', // empty == document.element.!!
52169     order : '001',
52170     name : 'xxxx'
52171     region : 'xxxx'
52172     disabled : function() {} 
52173      
52174     tree : function() { // return an tree of xtype declared components
52175         var MODULE = this;
52176         return 
52177         {
52178             xtype : 'NestedLayoutPanel',
52179             // technicall
52180         }
52181      ]
52182  *})
52183  *
52184  *
52185  * It can be used to build a big heiracy, with parent etc.
52186  * or you can just use this to render a single compoent to a dom element
52187  * MYPART.render(Roo.Element | String(id) | dom_element )
52188  * 
52189  * @extends Roo.util.Observable
52190  * @constructor
52191  * @param cfg {Object} configuration of component
52192  * 
52193  */
52194 Roo.XComponent = function(cfg) {
52195     Roo.apply(this, cfg);
52196     this.addEvents({ 
52197         /**
52198              * @event built
52199              * Fires when this the componnt is built
52200              * @param {Roo.XComponent} c the component
52201              */
52202         'built' : true,
52203         /**
52204              * @event buildcomplete
52205              * Fires on the top level element when all elements have been built
52206              * @param {Roo.XComponent} c the top level component.
52207          */
52208         'buildcomplete' : true
52209         
52210     });
52211     this.region = this.region || 'center'; // default..
52212     Roo.XComponent.register(this);
52213     this.modules = false;
52214     this.el = false; // where the layout goes..
52215     
52216     
52217 }
52218 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52219     /**
52220      * @property el
52221      * The created element (with Roo.factory())
52222      * @type {Roo.Layout}
52223      */
52224     el  : false,
52225     
52226     /**
52227      * @property el
52228      * for BC  - use el in new code
52229      * @type {Roo.Layout}
52230      */
52231     panel : false,
52232     
52233     /**
52234      * @property layout
52235      * for BC  - use el in new code
52236      * @type {Roo.Layout}
52237      */
52238     layout : false,
52239     
52240      /**
52241      * @cfg {Function|boolean} disabled
52242      * If this module is disabled by some rule, return true from the funtion
52243      */
52244     disabled : false,
52245     
52246     /**
52247      * @cfg {String} parent 
52248      * Name of parent element which it get xtype added to..
52249      */
52250     parent: false,
52251     
52252     /**
52253      * @cfg {String} order
52254      * Used to set the order in which elements are created (usefull for multiple tabs)
52255      */
52256     
52257     order : false,
52258     /**
52259      * @cfg {String} name
52260      * String to display while loading.
52261      */
52262     name : false,
52263     /**
52264      * @cfg {String} region
52265      * Region to render component to (defaults to center)
52266      */
52267     region : 'center',
52268     
52269     /**
52270      * @cfg {Array} items
52271      * A single item array - the first element is the root of the tree..
52272      * It's done this way to stay compatible with the Xtype system...
52273      */
52274     items : false,
52275     
52276     
52277      /**
52278      * render
52279      * render element to dom or tree
52280      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52281      */
52282     
52283     render : function(el)
52284     {
52285         
52286         el = el || false;
52287         var hp = this.parent ? 1 : 0;
52288         
52289         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52290             // if parent is a '#.....' string, then let's use that..
52291             var ename = this.parent.substr(1)
52292             this.parent = false;
52293             el = Roo.get(ename);
52294             if (!el) {
52295                 Roo.log("Warning - element can not be found :#" + ename );
52296                 return;
52297             }
52298         }
52299         
52300         
52301         if (!this.parent) {
52302             
52303             el = el ? Roo.get(el) : false;
52304             
52305             // it's a top level one..
52306             this.parent =  {
52307                 el : new Roo.BorderLayout(el || document.body, {
52308                 
52309                      center: {
52310                          titlebar: false,
52311                          autoScroll:false,
52312                          closeOnTab: true,
52313                          tabPosition: 'top',
52314                           //resizeTabs: true,
52315                          alwaysShowTabs: el && hp? false :  true,
52316                          hideTabs: el || !hp ? true :  false,
52317                          minTabWidth: 140
52318                      }
52319                  })
52320             }
52321         }
52322         
52323         
52324             
52325         var tree = this.tree();
52326         tree.region = tree.region || this.region;
52327         this.el = this.parent.el.addxtype(tree);
52328         this.fireEvent('built', this);
52329         
52330         this.panel = this.el;
52331         this.layout = this.panel.layout;    
52332          
52333     }
52334     
52335 });
52336
52337 Roo.apply(Roo.XComponent, {
52338     
52339     /**
52340      * @property  buildCompleted
52341      * True when the builder has completed building the interface.
52342      * @type Boolean
52343      */
52344     buildCompleted : false,
52345      
52346     /**
52347      * @property  topModule
52348      * the upper most module - uses document.element as it's constructor.
52349      * @type Object
52350      */
52351      
52352     topModule  : false,
52353       
52354     /**
52355      * @property  modules
52356      * array of modules to be created by registration system.
52357      * @type {Array} of Roo.XComponent
52358      */
52359     
52360     modules : [],
52361     /**
52362      * @property  elmodules
52363      * array of modules to be created by which use #ID 
52364      * @type {Array} of Roo.XComponent
52365      */
52366      
52367     elmodules : [],
52368
52369     
52370     /**
52371      * Register components to be built later.
52372      *
52373      * This solves the following issues
52374      * - Building is not done on page load, but after an authentication process has occured.
52375      * - Interface elements are registered on page load
52376      * - Parent Interface elements may not be loaded before child, so this handles that..
52377      * 
52378      *
52379      * example:
52380      * 
52381      * MyApp.register({
52382           order : '000001',
52383           module : 'Pman.Tab.projectMgr',
52384           region : 'center',
52385           parent : 'Pman.layout',
52386           disabled : false,  // or use a function..
52387         })
52388      
52389      * * @param {Object} details about module
52390      */
52391     register : function(obj) {
52392         this.modules.push(obj);
52393          
52394     },
52395     /**
52396      * convert a string to an object..
52397      * eg. 'AAA.BBB' -> finds AAA.BBB
52398
52399      */
52400     
52401     toObject : function(str)
52402     {
52403         if (!str || typeof(str) == 'object') {
52404             return str;
52405         }
52406         if (str.substring(0,1) == '#') {
52407             return str;
52408         }
52409
52410         var ar = str.split('.');
52411         var rt, o;
52412         rt = ar.shift();
52413             /** eval:var:o */
52414         try {
52415             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52416         } catch (e) {
52417             throw "Module not found : " + str;
52418         }
52419         
52420         if (o === false) {
52421             throw "Module not found : " + str;
52422         }
52423         Roo.each(ar, function(e) {
52424             if (typeof(o[e]) == 'undefined') {
52425                 throw "Module not found : " + str;
52426             }
52427             o = o[e];
52428         });
52429         
52430         return o;
52431         
52432     },
52433     
52434     
52435     /**
52436      * move modules into their correct place in the tree..
52437      * 
52438      */
52439     preBuild : function ()
52440     {
52441         var _t = this;
52442         Roo.each(this.modules , function (obj)
52443         {
52444             var opar = obj.parent;
52445             try { 
52446                 obj.parent = this.toObject(opar);
52447             } catch(e) {
52448                 Roo.log(e.toString());
52449                 return;
52450             }
52451             
52452             if (!obj.parent) {
52453                 this.topModule = obj;
52454                 return;
52455             }
52456             if (typeof(obj.parent) == 'string') {
52457                 this.elmodules.push(obj);
52458                 return;
52459             }
52460             if (obj.parent.constructor != Roo.XComponent) {
52461                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52462             }
52463             if (!obj.parent.modules) {
52464                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52465                     function(o) { return o.order + '' }
52466                 );
52467             }
52468             
52469             obj.parent.modules.add(obj);
52470         }, this);
52471     },
52472     
52473      /**
52474      * make a list of modules to build.
52475      * @return {Array} list of modules. 
52476      */ 
52477     
52478     buildOrder : function()
52479     {
52480         var _this = this;
52481         var cmp = function(a,b) {   
52482             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52483         };
52484         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52485             throw "No top level modules to build";
52486         }
52487         
52488         // make a flat list in order of modules to build.
52489         var mods = this.topModule ? [ this.topModule ] : [];
52490         Roo.each(this.elmodules,function(e) { mods.push(e) });
52491
52492         
52493         // add modules to their parents..
52494         var addMod = function(m) {
52495            // Roo.debug && Roo.log(m.modKey);
52496             
52497             mods.push(m);
52498             if (m.modules) {
52499                 m.modules.keySort('ASC',  cmp );
52500                 m.modules.each(addMod);
52501             }
52502             // not sure if this is used any more..
52503             if (m.finalize) {
52504                 m.finalize.name = m.name + " (clean up) ";
52505                 mods.push(m.finalize);
52506             }
52507             
52508         }
52509         if (this.topModule) { 
52510             this.topModule.modules.keySort('ASC',  cmp );
52511             this.topModule.modules.each(addMod);
52512         }
52513         return mods;
52514     },
52515     
52516      /**
52517      * Build the registered modules.
52518      * @param {Object} parent element.
52519      * @param {Function} optional method to call after module has been added.
52520      * 
52521      */ 
52522    
52523     build : function() 
52524     {
52525         
52526         this.preBuild();
52527         var mods = this.buildOrder();
52528       
52529         //this.allmods = mods;
52530         //Roo.debug && Roo.log(mods);
52531         //return;
52532         if (!mods.length) { // should not happen
52533             throw "NO modules!!!";
52534         }
52535         
52536         
52537         
52538         // flash it up as modal - so we store the mask!?
52539         Roo.MessageBox.show({ title: 'loading' });
52540         Roo.MessageBox.show({
52541            title: "Please wait...",
52542            msg: "Building Interface...",
52543            width:450,
52544            progress:true,
52545            closable:false,
52546            modal: false
52547           
52548         });
52549         var total = mods.length;
52550         
52551         var _this = this;
52552         var progressRun = function() {
52553             if (!mods.length) {
52554                 Roo.debug && Roo.log('hide?');
52555                 Roo.MessageBox.hide();
52556                 if (_this.topModule) { 
52557                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52558                 }
52559                 // THE END...
52560                 return false;   
52561             }
52562             
52563             var m = mods.shift();
52564             
52565             
52566             Roo.debug && Roo.log(m);
52567             // not sure if this is supported any more.. - modules that are are just function
52568             if (typeof(m) == 'function') { 
52569                 m.call(this);
52570                 return progressRun.defer(10, _this);
52571             } 
52572             
52573             
52574             
52575             Roo.MessageBox.updateProgress(
52576                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52577                     " of " + total + 
52578                     (m.name ? (' - ' + m.name) : '')
52579                     );
52580             
52581          
52582             // is the module disabled?
52583             var disabled = (typeof(m.disabled) == 'function') ?
52584                 m.disabled.call(m.module.disabled) : m.disabled;    
52585             
52586             
52587             if (disabled) {
52588                 return progressRun(); // we do not update the display!
52589             }
52590             
52591             // now build 
52592             
52593             m.render();
52594             // it's 10 on top level, and 1 on others??? why...
52595             return progressRun.defer(10, _this);
52596              
52597         }
52598         progressRun.defer(1, _this);
52599      
52600         
52601         
52602     }
52603     
52604      
52605    
52606     
52607     
52608 });
52609  //<script type="text/javascript">
52610
52611
52612 /**
52613  * @class Roo.Login
52614  * @extends Roo.LayoutDialog
52615  * A generic Login Dialog..... - only one needed in theory!?!?
52616  *
52617  * Fires XComponent builder on success...
52618  * 
52619  * Sends 
52620  *    username,password, lang = for login actions.
52621  *    check = 1 for periodic checking that sesion is valid.
52622  *    passwordRequest = email request password
52623  *    logout = 1 = to logout
52624  * 
52625  * Affects: (this id="????" elements)
52626  *   loading  (removed) (used to indicate application is loading)
52627  *   loading-mask (hides) (used to hide application when it's building loading)
52628  *   
52629  * 
52630  * Usage: 
52631  *    
52632  * 
52633  * Myapp.login = Roo.Login({
52634      url: xxxx,
52635    
52636      realm : 'Myapp', 
52637      
52638      
52639      method : 'POST',
52640      
52641      
52642      * 
52643  })
52644  * 
52645  * 
52646  * 
52647  **/
52648  
52649 Roo.Login = function(cfg)
52650 {
52651     this.addEvents({
52652         'refreshed' : true
52653     });
52654     
52655     Roo.apply(this,cfg);
52656     
52657     Roo.onReady(function() {
52658         this.onLoad();
52659     }, this);
52660     // call parent..
52661     
52662    
52663     Roo.Login.superclass.constructor.call(this, this);
52664     //this.addxtype(this.items[0]);
52665     
52666     
52667 }
52668
52669
52670 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52671     
52672     /**
52673      * @cfg {String} method
52674      * Method used to query for login details.
52675      */
52676     
52677     method : 'POST',
52678     /**
52679      * @cfg {String} url
52680      * URL to query login data. - eg. baseURL + '/Login.php'
52681      */
52682     url : '',
52683     
52684     /**
52685      * @property user
52686      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52687      * @type {Object} 
52688      */
52689     user : false,
52690     /**
52691      * @property checkFails
52692      * Number of times we have attempted to get authentication check, and failed.
52693      * @type {Number} 
52694      */
52695     checkFails : 0,
52696       /**
52697      * @property intervalID
52698      * The window interval that does the constant login checking.
52699      * @type {Number} 
52700      */
52701     intervalID : 0,
52702     
52703     
52704     onLoad : function() // called on page load...
52705     {
52706         // load 
52707          
52708         if (Roo.get('loading')) { // clear any loading indicator..
52709             Roo.get('loading').remove();
52710         }
52711         
52712         //this.switchLang('en'); // set the language to english..
52713        
52714         this.check({
52715             success:  function(response, opts)  {  // check successfull...
52716             
52717                 var res = this.processResponse(response);
52718                 this.checkFails =0;
52719                 if (!res.success) { // error!
52720                     this.checkFails = 5;
52721                     //console.log('call failure');
52722                     return this.failure(response,opts);
52723                 }
52724                 
52725                 if (!res.data.id) { // id=0 == login failure.
52726                     return this.show();
52727                 }
52728                 
52729                               
52730                         //console.log(success);
52731                 this.fillAuth(res.data);   
52732                 this.checkFails =0;
52733                 Roo.XComponent.build();
52734             },
52735             failure : this.show
52736         });
52737         
52738     }, 
52739     
52740     
52741     check: function(cfg) // called every so often to refresh cookie etc..
52742     {
52743         if (cfg.again) { // could be undefined..
52744             this.checkFails++;
52745         } else {
52746             this.checkFails = 0;
52747         }
52748         var _this = this;
52749         if (this.sending) {
52750             if ( this.checkFails > 4) {
52751                 Roo.MessageBox.alert("Error",  
52752                     "Error getting authentication status. - try reloading, or wait a while", function() {
52753                         _this.sending = false;
52754                     }); 
52755                 return;
52756             }
52757             cfg.again = true;
52758             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52759             return;
52760         }
52761         this.sending = true;
52762         
52763         Roo.Ajax.request({  
52764             url: this.url,
52765             params: {
52766                 getAuthUser: true
52767             },  
52768             method: this.method,
52769             success:  cfg.success || this.success,
52770             failure : cfg.failure || this.failure,
52771             scope : this,
52772             callCfg : cfg
52773               
52774         });  
52775     }, 
52776     
52777     
52778     logout: function()
52779     {
52780         window.onbeforeunload = function() { }; // false does not work for IE..
52781         this.user = false;
52782         var _this = this;
52783         
52784         Roo.Ajax.request({  
52785             url: this.url,
52786             params: {
52787                 logout: 1
52788             },  
52789             method: 'GET',
52790             failure : function() {
52791                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52792                     document.location = document.location.toString() + '?ts=' + Math.random();
52793                 });
52794                 
52795             },
52796             success : function() {
52797                 _this.user = false;
52798                 this.checkFails =0;
52799                 // fixme..
52800                 document.location = document.location.toString() + '?ts=' + Math.random();
52801             }
52802               
52803               
52804         }); 
52805     },
52806     
52807     processResponse : function (response)
52808     {
52809         var res = '';
52810         try {
52811             res = Roo.decode(response.responseText);
52812             // oops...
52813             if (typeof(res) != 'object') {
52814                 res = { success : false, errorMsg : res, errors : true };
52815             }
52816             if (typeof(res.success) == 'undefined') {
52817                 res.success = false;
52818             }
52819             
52820         } catch(e) {
52821             res = { success : false,  errorMsg : response.responseText, errors : true };
52822         }
52823         return res;
52824     },
52825     
52826     success : function(response, opts)  // check successfull...
52827     {  
52828         this.sending = false;
52829         var res = this.processResponse(response);
52830         if (!res.success) {
52831             return this.failure(response, opts);
52832         }
52833         if (!res.data || !res.data.id) {
52834             return this.failure(response,opts);
52835         }
52836         //console.log(res);
52837         this.fillAuth(res.data);
52838         
52839         this.checkFails =0;
52840         
52841     },
52842     
52843     
52844     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52845     {
52846         this.authUser = -1;
52847         this.sending = false;
52848         var res = this.processResponse(response);
52849         //console.log(res);
52850         if ( this.checkFails > 2) {
52851         
52852             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52853                 "Error getting authentication status. - try reloading"); 
52854             return;
52855         }
52856         opts.callCfg.again = true;
52857         this.check.defer(1000, this, [ opts.callCfg ]);
52858         return;  
52859     },
52860     
52861     
52862     
52863     fillAuth: function(au) {
52864         this.startAuthCheck();
52865         this.authUserId = au.id;
52866         this.authUser = au;
52867         this.lastChecked = new Date();
52868         this.fireEvent('refreshed', au);
52869         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52870         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52871         au.lang = au.lang || 'en';
52872         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52873         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52874         this.switchLang(au.lang );
52875         
52876      
52877         // open system... - -on setyp..
52878         if (this.authUserId  < 0) {
52879             Roo.MessageBox.alert("Warning", 
52880                 "This is an open system - please set up a admin user with a password.");  
52881         }
52882          
52883         //Pman.onload(); // which should do nothing if it's a re-auth result...
52884         
52885              
52886     },
52887     
52888     startAuthCheck : function() // starter for timeout checking..
52889     {
52890         if (this.intervalID) { // timer already in place...
52891             return false;
52892         }
52893         var _this = this;
52894         this.intervalID =  window.setInterval(function() {
52895               _this.check(false);
52896             }, 120000); // every 120 secs = 2mins..
52897         
52898         
52899     },
52900          
52901     
52902     switchLang : function (lang) 
52903     {
52904         _T = typeof(_T) == 'undefined' ? false : _T;
52905           if (!_T || !lang.length) {
52906             return;
52907         }
52908         
52909         if (!_T && lang != 'en') {
52910             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52911             return;
52912         }
52913         
52914         if (typeof(_T.en) == 'undefined') {
52915             _T.en = {};
52916             Roo.apply(_T.en, _T);
52917         }
52918         
52919         if (typeof(_T[lang]) == 'undefined') {
52920             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52921             return;
52922         }
52923         
52924         
52925         Roo.apply(_T, _T[lang]);
52926         // just need to set the text values for everything...
52927         var _this = this;
52928         /* this will not work ...
52929         if (this.form) { 
52930             
52931                
52932             function formLabel(name, val) {
52933                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52934             }
52935             
52936             formLabel('password', "Password"+':');
52937             formLabel('username', "Email Address"+':');
52938             formLabel('lang', "Language"+':');
52939             this.dialog.setTitle("Login");
52940             this.dialog.buttons[0].setText("Forgot Password");
52941             this.dialog.buttons[1].setText("Login");
52942         }
52943         */
52944         
52945         
52946     },
52947     
52948     
52949     title: "Login",
52950     modal: true,
52951     width:  350,
52952     //height: 230,
52953     height: 180,
52954     shadow: true,
52955     minWidth:200,
52956     minHeight:180,
52957     //proxyDrag: true,
52958     closable: false,
52959     draggable: false,
52960     collapsible: false,
52961     resizable: false,
52962     center: {  // needed??
52963         autoScroll:false,
52964         titlebar: false,
52965        // tabPosition: 'top',
52966         hideTabs: true,
52967         closeOnTab: true,
52968         alwaysShowTabs: false
52969     } ,
52970     listeners : {
52971         
52972         show  : function(dlg)
52973         {
52974             //console.log(this);
52975             this.form = this.layout.getRegion('center').activePanel.form;
52976             this.form.dialog = dlg;
52977             this.buttons[0].form = this.form;
52978             this.buttons[0].dialog = dlg;
52979             this.buttons[1].form = this.form;
52980             this.buttons[1].dialog = dlg;
52981            
52982            //this.resizeToLogo.defer(1000,this);
52983             // this is all related to resizing for logos..
52984             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52985            //// if (!sz) {
52986              //   this.resizeToLogo.defer(1000,this);
52987              //   return;
52988            // }
52989             //var w = Ext.lib.Dom.getViewWidth() - 100;
52990             //var h = Ext.lib.Dom.getViewHeight() - 100;
52991             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52992             //this.center();
52993             if (this.disabled) {
52994                 this.hide();
52995                 return;
52996             }
52997             
52998             if (this.user.id < 0) { // used for inital setup situations.
52999                 return;
53000             }
53001             
53002             if (this.intervalID) {
53003                 // remove the timer
53004                 window.clearInterval(this.intervalID);
53005                 this.intervalID = false;
53006             }
53007             
53008             
53009             if (Roo.get('loading')) {
53010                 Roo.get('loading').remove();
53011             }
53012             if (Roo.get('loading-mask')) {
53013                 Roo.get('loading-mask').hide();
53014             }
53015             
53016             //incomming._node = tnode;
53017             this.form.reset();
53018             //this.dialog.modal = !modal;
53019             //this.dialog.show();
53020             this.el.unmask(); 
53021             
53022             
53023             this.form.setValues({
53024                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53025                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53026             });
53027             
53028             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53029             if (this.form.findField('username').getValue().length > 0 ){
53030                 this.form.findField('password').focus();
53031             } else {
53032                this.form.findField('username').focus();
53033             }
53034     
53035         }
53036     },
53037     items : [
53038          {
53039        
53040             xtype : 'ContentPanel',
53041             xns : Roo,
53042             region: 'center',
53043             fitToFrame : true,
53044             
53045             items : [
53046     
53047                 {
53048                
53049                     xtype : 'Form',
53050                     xns : Roo.form,
53051                     labelWidth: 100,
53052                     style : 'margin: 10px;',
53053                     
53054                     listeners : {
53055                         actionfailed : function(f, act) {
53056                             // form can return { errors: .... }
53057                                 
53058                             //act.result.errors // invalid form element list...
53059                             //act.result.errorMsg// invalid form element list...
53060                             
53061                             this.dialog.el.unmask();
53062                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53063                                         "Login failed - communication error - try again.");
53064                                       
53065                         },
53066                         actioncomplete: function(re, act) {
53067                              
53068                             Roo.state.Manager.set(
53069                                 this.dialog.realm + '.username',  
53070                                     this.findField('username').getValue()
53071                             );
53072                             Roo.state.Manager.set(
53073                                 this.dialog.realm + '.lang',  
53074                                 this.findField('lang').getValue() 
53075                             );
53076                             
53077                             this.dialog.fillAuth(act.result.data);
53078                               
53079                             this.dialog.hide();
53080                             
53081                             if (Roo.get('loading-mask')) {
53082                                 Roo.get('loading-mask').show();
53083                             }
53084                             Roo.XComponent.build();
53085                             
53086                              
53087                             
53088                         }
53089                     },
53090                     items : [
53091                         {
53092                             xtype : 'TextField',
53093                             xns : Roo.form,
53094                             fieldLabel: "Email Address",
53095                             name: 'username',
53096                             width:200,
53097                             autoCreate : {tag: "input", type: "text", size: "20"}
53098                         },
53099                         {
53100                             xtype : 'TextField',
53101                             xns : Roo.form,
53102                             fieldLabel: "Password",
53103                             inputType: 'password',
53104                             name: 'password',
53105                             width:200,
53106                             autoCreate : {tag: "input", type: "text", size: "20"},
53107                             listeners : {
53108                                 specialkey : function(e,ev) {
53109                                     if (ev.keyCode == 13) {
53110                                         this.form.dialog.el.mask("Logging in");
53111                                         this.form.doAction('submit', {
53112                                             url: this.form.dialog.url,
53113                                             method: this.form.dialog.method
53114                                         });
53115                                     }
53116                                 }
53117                             }  
53118                         },
53119                         {
53120                             xtype : 'ComboBox',
53121                             xns : Roo.form,
53122                             fieldLabel: "Language",
53123                             name : 'langdisp',
53124                             store: {
53125                                 xtype : 'SimpleStore',
53126                                 fields: ['lang', 'ldisp'],
53127                                 data : [
53128                                     [ 'en', 'English' ],
53129                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53130                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53131                                 ]
53132                             },
53133                             
53134                             valueField : 'lang',
53135                             hiddenName:  'lang',
53136                             width: 200,
53137                             displayField:'ldisp',
53138                             typeAhead: false,
53139                             editable: false,
53140                             mode: 'local',
53141                             triggerAction: 'all',
53142                             emptyText:'Select a Language...',
53143                             selectOnFocus:true,
53144                             listeners : {
53145                                 select :  function(cb, rec, ix) {
53146                                     this.form.switchLang(rec.data.lang);
53147                                 }
53148                             }
53149                         
53150                         }
53151                     ]
53152                 }
53153                   
53154                 
53155             ]
53156         }
53157     ],
53158     buttons : [
53159         {
53160             xtype : 'Button',
53161             xns : 'Roo',
53162             text : "Forgot Password",
53163             listeners : {
53164                 click : function() {
53165                     //console.log(this);
53166                     var n = this.form.findField('username').getValue();
53167                     if (!n.length) {
53168                         Roo.MessageBox.alert("Error", "Fill in your email address");
53169                         return;
53170                     }
53171                     Roo.Ajax.request({
53172                         url: this.dialog.url,
53173                         params: {
53174                             passwordRequest: n
53175                         },
53176                         method: this.dialog.method,
53177                         success:  function(response, opts)  {  // check successfull...
53178                         
53179                             var res = this.dialog.processResponse(response);
53180                             if (!res.success) { // error!
53181                                Roo.MessageBox.alert("Error" ,
53182                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53183                                return;
53184                             }
53185                             Roo.MessageBox.alert("Notice" ,
53186                                 "Please check you email for the Password Reset message");
53187                         },
53188                         failure : function() {
53189                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53190                         }
53191                         
53192                     });
53193                 }
53194             }
53195         },
53196         {
53197             xtype : 'Button',
53198             xns : 'Roo',
53199             text : "Login",
53200             listeners : {
53201                 
53202                 click : function () {
53203                         
53204                     this.dialog.el.mask("Logging in");
53205                     this.form.doAction('submit', {
53206                             url: this.dialog.url,
53207                             method: this.dialog.method
53208                     });
53209                 }
53210             }
53211         }
53212     ]
53213   
53214   
53215 })
53216  
53217
53218
53219