roojs-ui-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             // show a message if no listener is registered.
19557             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19558                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19559             }
19560             // loadmask wil be hooked into this..
19561             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19562             return;
19563         }
19564         var r = o.records, t = o.totalRecords || r.length;
19565         if(!options || options.add !== true){
19566             if(this.pruneModifiedRecords){
19567                 this.modified = [];
19568             }
19569             for(var i = 0, len = r.length; i < len; i++){
19570                 r[i].join(this);
19571             }
19572             if(this.snapshot){
19573                 this.data = this.snapshot;
19574                 delete this.snapshot;
19575             }
19576             this.data.clear();
19577             this.data.addAll(r);
19578             this.totalLength = t;
19579             this.applySort();
19580             this.fireEvent("datachanged", this);
19581         }else{
19582             this.totalLength = Math.max(t, this.data.length+r.length);
19583             this.add(r);
19584         }
19585         this.fireEvent("load", this, r, options);
19586         if(options.callback){
19587             options.callback.call(options.scope || this, r, options, true);
19588         }
19589     },
19590
19591
19592     /**
19593      * Loads data from a passed data block. A Reader which understands the format of the data
19594      * must have been configured in the constructor.
19595      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19596      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19597      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19598      */
19599     loadData : function(o, append){
19600         var r = this.reader.readRecords(o);
19601         this.loadRecords(r, {add: append}, true);
19602     },
19603
19604     /**
19605      * Gets the number of cached records.
19606      * <p>
19607      * <em>If using paging, this may not be the total size of the dataset. If the data object
19608      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19609      * the data set size</em>
19610      */
19611     getCount : function(){
19612         return this.data.length || 0;
19613     },
19614
19615     /**
19616      * Gets the total number of records in the dataset as returned by the server.
19617      * <p>
19618      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19619      * the dataset size</em>
19620      */
19621     getTotalCount : function(){
19622         return this.totalLength || 0;
19623     },
19624
19625     /**
19626      * Returns the sort state of the Store as an object with two properties:
19627      * <pre><code>
19628  field {String} The name of the field by which the Records are sorted
19629  direction {String} The sort order, "ASC" or "DESC"
19630      * </code></pre>
19631      */
19632     getSortState : function(){
19633         return this.sortInfo;
19634     },
19635
19636     // private
19637     applySort : function(){
19638         if(this.sortInfo && !this.remoteSort){
19639             var s = this.sortInfo, f = s.field;
19640             var st = this.fields.get(f).sortType;
19641             var fn = function(r1, r2){
19642                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19643                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19644             };
19645             this.data.sort(s.direction, fn);
19646             if(this.snapshot && this.snapshot != this.data){
19647                 this.snapshot.sort(s.direction, fn);
19648             }
19649         }
19650     },
19651
19652     /**
19653      * Sets the default sort column and order to be used by the next load operation.
19654      * @param {String} fieldName The name of the field to sort by.
19655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19656      */
19657     setDefaultSort : function(field, dir){
19658         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19659     },
19660
19661     /**
19662      * Sort the Records.
19663      * If remote sorting is used, the sort is performed on the server, and the cache is
19664      * reloaded. If local sorting is used, the cache is sorted internally.
19665      * @param {String} fieldName The name of the field to sort by.
19666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19667      */
19668     sort : function(fieldName, dir){
19669         var f = this.fields.get(fieldName);
19670         if(!dir){
19671             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19672             
19673             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19674                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19675             }else{
19676                 dir = f.sortDir;
19677             }
19678         }
19679         this.sortToggle[f.name] = dir;
19680         this.sortInfo = {field: f.name, direction: dir};
19681         if(!this.remoteSort){
19682             this.applySort();
19683             this.fireEvent("datachanged", this);
19684         }else{
19685             this.load(this.lastOptions);
19686         }
19687     },
19688
19689     /**
19690      * Calls the specified function for each of the Records in the cache.
19691      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19692      * Returning <em>false</em> aborts and exits the iteration.
19693      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19694      */
19695     each : function(fn, scope){
19696         this.data.each(fn, scope);
19697     },
19698
19699     /**
19700      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19701      * (e.g., during paging).
19702      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19703      */
19704     getModifiedRecords : function(){
19705         return this.modified;
19706     },
19707
19708     // private
19709     createFilterFn : function(property, value, anyMatch){
19710         if(!value.exec){ // not a regex
19711             value = String(value);
19712             if(value.length == 0){
19713                 return false;
19714             }
19715             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19716         }
19717         return function(r){
19718             return value.test(r.data[property]);
19719         };
19720     },
19721
19722     /**
19723      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19724      * @param {String} property A field on your records
19725      * @param {Number} start The record index to start at (defaults to 0)
19726      * @param {Number} end The last record index to include (defaults to length - 1)
19727      * @return {Number} The sum
19728      */
19729     sum : function(property, start, end){
19730         var rs = this.data.items, v = 0;
19731         start = start || 0;
19732         end = (end || end === 0) ? end : rs.length-1;
19733
19734         for(var i = start; i <= end; i++){
19735             v += (rs[i].data[property] || 0);
19736         }
19737         return v;
19738     },
19739
19740     /**
19741      * Filter the records by a specified property.
19742      * @param {String} field A field on your records
19743      * @param {String/RegExp} value Either a string that the field
19744      * should start with or a RegExp to test against the field
19745      * @param {Boolean} anyMatch True to match any part not just the beginning
19746      */
19747     filter : function(property, value, anyMatch){
19748         var fn = this.createFilterFn(property, value, anyMatch);
19749         return fn ? this.filterBy(fn) : this.clearFilter();
19750     },
19751
19752     /**
19753      * Filter by a function. The specified function will be called with each
19754      * record in this data source. If the function returns true the record is included,
19755      * otherwise it is filtered.
19756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19757      * @param {Object} scope (optional) The scope of the function (defaults to this)
19758      */
19759     filterBy : function(fn, scope){
19760         this.snapshot = this.snapshot || this.data;
19761         this.data = this.queryBy(fn, scope||this);
19762         this.fireEvent("datachanged", this);
19763     },
19764
19765     /**
19766      * Query the records by a specified property.
19767      * @param {String} field A field on your records
19768      * @param {String/RegExp} value Either a string that the field
19769      * should start with or a RegExp to test against the field
19770      * @param {Boolean} anyMatch True to match any part not just the beginning
19771      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19772      */
19773     query : function(property, value, anyMatch){
19774         var fn = this.createFilterFn(property, value, anyMatch);
19775         return fn ? this.queryBy(fn) : this.data.clone();
19776     },
19777
19778     /**
19779      * Query by a function. The specified function will be called with each
19780      * record in this data source. If the function returns true the record is included
19781      * in the results.
19782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19783      * @param {Object} scope (optional) The scope of the function (defaults to this)
19784       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19785      **/
19786     queryBy : function(fn, scope){
19787         var data = this.snapshot || this.data;
19788         return data.filterBy(fn, scope||this);
19789     },
19790
19791     /**
19792      * Collects unique values for a particular dataIndex from this store.
19793      * @param {String} dataIndex The property to collect
19794      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19795      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19796      * @return {Array} An array of the unique values
19797      **/
19798     collect : function(dataIndex, allowNull, bypassFilter){
19799         var d = (bypassFilter === true && this.snapshot) ?
19800                 this.snapshot.items : this.data.items;
19801         var v, sv, r = [], l = {};
19802         for(var i = 0, len = d.length; i < len; i++){
19803             v = d[i].data[dataIndex];
19804             sv = String(v);
19805             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19806                 l[sv] = true;
19807                 r[r.length] = v;
19808             }
19809         }
19810         return r;
19811     },
19812
19813     /**
19814      * Revert to a view of the Record cache with no filtering applied.
19815      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19816      */
19817     clearFilter : function(suppressEvent){
19818         if(this.snapshot && this.snapshot != this.data){
19819             this.data = this.snapshot;
19820             delete this.snapshot;
19821             if(suppressEvent !== true){
19822                 this.fireEvent("datachanged", this);
19823             }
19824         }
19825     },
19826
19827     // private
19828     afterEdit : function(record){
19829         if(this.modified.indexOf(record) == -1){
19830             this.modified.push(record);
19831         }
19832         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19833     },
19834     
19835     // private
19836     afterReject : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19839     },
19840
19841     // private
19842     afterCommit : function(record){
19843         this.modified.remove(record);
19844         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19845     },
19846
19847     /**
19848      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19849      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19850      */
19851     commitChanges : function(){
19852         var m = this.modified.slice(0);
19853         this.modified = [];
19854         for(var i = 0, len = m.length; i < len; i++){
19855             m[i].commit();
19856         }
19857     },
19858
19859     /**
19860      * Cancel outstanding changes on all changed records.
19861      */
19862     rejectChanges : function(){
19863         var m = this.modified.slice(0);
19864         this.modified = [];
19865         for(var i = 0, len = m.length; i < len; i++){
19866             m[i].reject();
19867         }
19868     },
19869
19870     onMetaChange : function(meta, rtype, o){
19871         this.recordType = rtype;
19872         this.fields = rtype.prototype.fields;
19873         delete this.snapshot;
19874         this.sortInfo = meta.sortInfo || this.sortInfo;
19875         this.modified = [];
19876         this.fireEvent('metachange', this, this.reader.meta);
19877     }
19878 });/*
19879  * Based on:
19880  * Ext JS Library 1.1.1
19881  * Copyright(c) 2006-2007, Ext JS, LLC.
19882  *
19883  * Originally Released Under LGPL - original licence link has changed is not relivant.
19884  *
19885  * Fork - LGPL
19886  * <script type="text/javascript">
19887  */
19888
19889 /**
19890  * @class Roo.data.SimpleStore
19891  * @extends Roo.data.Store
19892  * Small helper class to make creating Stores from Array data easier.
19893  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19894  * @cfg {Array} fields An array of field definition objects, or field name strings.
19895  * @cfg {Array} data The multi-dimensional array of data
19896  * @constructor
19897  * @param {Object} config
19898  */
19899 Roo.data.SimpleStore = function(config){
19900     Roo.data.SimpleStore.superclass.constructor.call(this, {
19901         isLocal : true,
19902         reader: new Roo.data.ArrayReader({
19903                 id: config.id
19904             },
19905             Roo.data.Record.create(config.fields)
19906         ),
19907         proxy : new Roo.data.MemoryProxy(config.data)
19908     });
19909     this.load();
19910 };
19911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19912  * Based on:
19913  * Ext JS Library 1.1.1
19914  * Copyright(c) 2006-2007, Ext JS, LLC.
19915  *
19916  * Originally Released Under LGPL - original licence link has changed is not relivant.
19917  *
19918  * Fork - LGPL
19919  * <script type="text/javascript">
19920  */
19921
19922 /**
19923 /**
19924  * @extends Roo.data.Store
19925  * @class Roo.data.JsonStore
19926  * Small helper class to make creating Stores for JSON data easier. <br/>
19927 <pre><code>
19928 var store = new Roo.data.JsonStore({
19929     url: 'get-images.php',
19930     root: 'images',
19931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19932 });
19933 </code></pre>
19934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19935  * JsonReader and HttpProxy (unless inline data is provided).</b>
19936  * @cfg {Array} fields An array of field definition objects, or field name strings.
19937  * @constructor
19938  * @param {Object} config
19939  */
19940 Roo.data.JsonStore = function(c){
19941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19943         reader: new Roo.data.JsonReader(c, c.fields)
19944     }));
19945 };
19946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19947  * Based on:
19948  * Ext JS Library 1.1.1
19949  * Copyright(c) 2006-2007, Ext JS, LLC.
19950  *
19951  * Originally Released Under LGPL - original licence link has changed is not relivant.
19952  *
19953  * Fork - LGPL
19954  * <script type="text/javascript">
19955  */
19956
19957  
19958 Roo.data.Field = function(config){
19959     if(typeof config == "string"){
19960         config = {name: config};
19961     }
19962     Roo.apply(this, config);
19963     
19964     if(!this.type){
19965         this.type = "auto";
19966     }
19967     
19968     var st = Roo.data.SortTypes;
19969     // named sortTypes are supported, here we look them up
19970     if(typeof this.sortType == "string"){
19971         this.sortType = st[this.sortType];
19972     }
19973     
19974     // set default sortType for strings and dates
19975     if(!this.sortType){
19976         switch(this.type){
19977             case "string":
19978                 this.sortType = st.asUCString;
19979                 break;
19980             case "date":
19981                 this.sortType = st.asDate;
19982                 break;
19983             default:
19984                 this.sortType = st.none;
19985         }
19986     }
19987
19988     // define once
19989     var stripRe = /[\$,%]/g;
19990
19991     // prebuilt conversion function for this field, instead of
19992     // switching every time we're reading a value
19993     if(!this.convert){
19994         var cv, dateFormat = this.dateFormat;
19995         switch(this.type){
19996             case "":
19997             case "auto":
19998             case undefined:
19999                 cv = function(v){ return v; };
20000                 break;
20001             case "string":
20002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20003                 break;
20004             case "int":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20008                     };
20009                 break;
20010             case "float":
20011                 cv = function(v){
20012                     return v !== undefined && v !== null && v !== '' ?
20013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20014                     };
20015                 break;
20016             case "bool":
20017             case "boolean":
20018                 cv = function(v){ return v === true || v === "true" || v == 1; };
20019                 break;
20020             case "date":
20021                 cv = function(v){
20022                     if(!v){
20023                         return '';
20024                     }
20025                     if(v instanceof Date){
20026                         return v;
20027                     }
20028                     if(dateFormat){
20029                         if(dateFormat == "timestamp"){
20030                             return new Date(v*1000);
20031                         }
20032                         return Date.parseDate(v, dateFormat);
20033                     }
20034                     var parsed = Date.parse(v);
20035                     return parsed ? new Date(parsed) : null;
20036                 };
20037              break;
20038             
20039         }
20040         this.convert = cv;
20041     }
20042 };
20043
20044 Roo.data.Field.prototype = {
20045     dateFormat: null,
20046     defaultValue: "",
20047     mapping: null,
20048     sortType : null,
20049     sortDir : "ASC"
20050 };/*
20051  * Based on:
20052  * Ext JS Library 1.1.1
20053  * Copyright(c) 2006-2007, Ext JS, LLC.
20054  *
20055  * Originally Released Under LGPL - original licence link has changed is not relivant.
20056  *
20057  * Fork - LGPL
20058  * <script type="text/javascript">
20059  */
20060  
20061 // Base class for reading structured data from a data source.  This class is intended to be
20062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20063
20064 /**
20065  * @class Roo.data.DataReader
20066  * Base class for reading structured data from a data source.  This class is intended to be
20067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20068  */
20069
20070 Roo.data.DataReader = function(meta, recordType){
20071     
20072     this.meta = meta;
20073     
20074     this.recordType = recordType instanceof Array ? 
20075         Roo.data.Record.create(recordType) : recordType;
20076 };
20077
20078 Roo.data.DataReader.prototype = {
20079      /**
20080      * Create an empty record
20081      * @param {Object} data (optional) - overlay some values
20082      * @return {Roo.data.Record} record created.
20083      */
20084     newRow :  function(d) {
20085         var da =  {};
20086         this.recordType.prototype.fields.each(function(c) {
20087             switch( c.type) {
20088                 case 'int' : da[c.name] = 0; break;
20089                 case 'date' : da[c.name] = new Date(); break;
20090                 case 'float' : da[c.name] = 0.0; break;
20091                 case 'boolean' : da[c.name] = false; break;
20092                 default : da[c.name] = ""; break;
20093             }
20094             
20095         });
20096         return new this.recordType(Roo.apply(da, d));
20097     }
20098     
20099 };/*
20100  * Based on:
20101  * Ext JS Library 1.1.1
20102  * Copyright(c) 2006-2007, Ext JS, LLC.
20103  *
20104  * Originally Released Under LGPL - original licence link has changed is not relivant.
20105  *
20106  * Fork - LGPL
20107  * <script type="text/javascript">
20108  */
20109
20110 /**
20111  * @class Roo.data.DataProxy
20112  * @extends Roo.data.Observable
20113  * This class is an abstract base class for implementations which provide retrieval of
20114  * unformatted data objects.<br>
20115  * <p>
20116  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20117  * (of the appropriate type which knows how to parse the data object) to provide a block of
20118  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20119  * <p>
20120  * Custom implementations must implement the load method as described in
20121  * {@link Roo.data.HttpProxy#load}.
20122  */
20123 Roo.data.DataProxy = function(){
20124     this.addEvents({
20125         /**
20126          * @event beforeload
20127          * Fires before a network request is made to retrieve a data object.
20128          * @param {Object} This DataProxy object.
20129          * @param {Object} params The params parameter to the load function.
20130          */
20131         beforeload : true,
20132         /**
20133          * @event load
20134          * Fires before the load method's callback is called.
20135          * @param {Object} This DataProxy object.
20136          * @param {Object} o The data object.
20137          * @param {Object} arg The callback argument object passed to the load function.
20138          */
20139         load : true,
20140         /**
20141          * @event loadexception
20142          * Fires if an Exception occurs during data retrieval.
20143          * @param {Object} This DataProxy object.
20144          * @param {Object} o The data object.
20145          * @param {Object} arg The callback argument object passed to the load function.
20146          * @param {Object} e The Exception.
20147          */
20148         loadexception : true
20149     });
20150     Roo.data.DataProxy.superclass.constructor.call(this);
20151 };
20152
20153 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20154
20155     /**
20156      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20157      */
20158 /*
20159  * Based on:
20160  * Ext JS Library 1.1.1
20161  * Copyright(c) 2006-2007, Ext JS, LLC.
20162  *
20163  * Originally Released Under LGPL - original licence link has changed is not relivant.
20164  *
20165  * Fork - LGPL
20166  * <script type="text/javascript">
20167  */
20168 /**
20169  * @class Roo.data.MemoryProxy
20170  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20171  * to the Reader when its load method is called.
20172  * @constructor
20173  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20174  */
20175 Roo.data.MemoryProxy = function(data){
20176     if (data.data) {
20177         data = data.data;
20178     }
20179     Roo.data.MemoryProxy.superclass.constructor.call(this);
20180     this.data = data;
20181 };
20182
20183 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20184     /**
20185      * Load data from the requested source (in this case an in-memory
20186      * data object passed to the constructor), read the data object into
20187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20188      * process that block using the passed callback.
20189      * @param {Object} params This parameter is not used by the MemoryProxy class.
20190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20191      * object into a block of Roo.data.Records.
20192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20193      * The function must be passed <ul>
20194      * <li>The Record block object</li>
20195      * <li>The "arg" argument from the load function</li>
20196      * <li>A boolean success indicator</li>
20197      * </ul>
20198      * @param {Object} scope The scope in which to call the callback
20199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20200      */
20201     load : function(params, reader, callback, scope, arg){
20202         params = params || {};
20203         var result;
20204         try {
20205             result = reader.readRecords(this.data);
20206         }catch(e){
20207             this.fireEvent("loadexception", this, arg, null, e);
20208             callback.call(scope, null, arg, false);
20209             return;
20210         }
20211         callback.call(scope, result, arg, true);
20212     },
20213     
20214     // private
20215     update : function(params, records){
20216         
20217     }
20218 });/*
20219  * Based on:
20220  * Ext JS Library 1.1.1
20221  * Copyright(c) 2006-2007, Ext JS, LLC.
20222  *
20223  * Originally Released Under LGPL - original licence link has changed is not relivant.
20224  *
20225  * Fork - LGPL
20226  * <script type="text/javascript">
20227  */
20228 /**
20229  * @class Roo.data.HttpProxy
20230  * @extends Roo.data.DataProxy
20231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20232  * configured to reference a certain URL.<br><br>
20233  * <p>
20234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20235  * from which the running page was served.<br><br>
20236  * <p>
20237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20238  * <p>
20239  * Be aware that to enable the browser to parse an XML document, the server must set
20240  * the Content-Type header in the HTTP response to "text/xml".
20241  * @constructor
20242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20244  * will be used to make the request.
20245  */
20246 Roo.data.HttpProxy = function(conn){
20247     Roo.data.HttpProxy.superclass.constructor.call(this);
20248     // is conn a conn config or a real conn?
20249     this.conn = conn;
20250     this.useAjax = !conn || !conn.events;
20251   
20252 };
20253
20254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20255     // thse are take from connection...
20256     
20257     /**
20258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20259      */
20260     /**
20261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20262      * extra parameters to each request made by this object. (defaults to undefined)
20263      */
20264     /**
20265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20266      *  to each request made by this object. (defaults to undefined)
20267      */
20268     /**
20269      * @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)
20270      */
20271     /**
20272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20273      */
20274      /**
20275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20276      * @type Boolean
20277      */
20278   
20279
20280     /**
20281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20282      * @type Boolean
20283      */
20284     /**
20285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20287      * a finer-grained basis than the DataProxy events.
20288      */
20289     getConnection : function(){
20290         return this.useAjax ? Roo.Ajax : this.conn;
20291     },
20292
20293     /**
20294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20296      * process that block using the passed callback.
20297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20298      * for the request to the remote server.
20299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20300      * object into a block of Roo.data.Records.
20301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20302      * The function must be passed <ul>
20303      * <li>The Record block object</li>
20304      * <li>The "arg" argument from the load function</li>
20305      * <li>A boolean success indicator</li>
20306      * </ul>
20307      * @param {Object} scope The scope in which to call the callback
20308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20309      */
20310     load : function(params, reader, callback, scope, arg){
20311         if(this.fireEvent("beforeload", this, params) !== false){
20312             var  o = {
20313                 params : params || {},
20314                 request: {
20315                     callback : callback,
20316                     scope : scope,
20317                     arg : arg
20318                 },
20319                 reader: reader,
20320                 callback : this.loadResponse,
20321                 scope: this
20322             };
20323             if(this.useAjax){
20324                 Roo.applyIf(o, this.conn);
20325                 if(this.activeRequest){
20326                     Roo.Ajax.abort(this.activeRequest);
20327                 }
20328                 this.activeRequest = Roo.Ajax.request(o);
20329             }else{
20330                 this.conn.request(o);
20331             }
20332         }else{
20333             callback.call(scope||this, null, arg, false);
20334         }
20335     },
20336
20337     // private
20338     loadResponse : function(o, success, response){
20339         delete this.activeRequest;
20340         if(!success){
20341             this.fireEvent("loadexception", this, o, response);
20342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20343             return;
20344         }
20345         var result;
20346         try {
20347             result = o.reader.read(response);
20348         }catch(e){
20349             this.fireEvent("loadexception", this, o, response, e);
20350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20351             return;
20352         }
20353         
20354         this.fireEvent("load", this, o, o.request.arg);
20355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20356     },
20357
20358     // private
20359     update : function(dataSet){
20360
20361     },
20362
20363     // private
20364     updateResponse : function(dataSet){
20365
20366     }
20367 });/*
20368  * Based on:
20369  * Ext JS Library 1.1.1
20370  * Copyright(c) 2006-2007, Ext JS, LLC.
20371  *
20372  * Originally Released Under LGPL - original licence link has changed is not relivant.
20373  *
20374  * Fork - LGPL
20375  * <script type="text/javascript">
20376  */
20377
20378 /**
20379  * @class Roo.data.ScriptTagProxy
20380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20381  * other than the originating domain of the running page.<br><br>
20382  * <p>
20383  * <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
20384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20385  * <p>
20386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20387  * source code that is used as the source inside a &lt;script> tag.<br><br>
20388  * <p>
20389  * In order for the browser to process the returned data, the server must wrap the data object
20390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20392  * depending on whether the callback name was passed:
20393  * <p>
20394  * <pre><code>
20395 boolean scriptTag = false;
20396 String cb = request.getParameter("callback");
20397 if (cb != null) {
20398     scriptTag = true;
20399     response.setContentType("text/javascript");
20400 } else {
20401     response.setContentType("application/x-json");
20402 }
20403 Writer out = response.getWriter();
20404 if (scriptTag) {
20405     out.write(cb + "(");
20406 }
20407 out.print(dataBlock.toJsonString());
20408 if (scriptTag) {
20409     out.write(");");
20410 }
20411 </pre></code>
20412  *
20413  * @constructor
20414  * @param {Object} config A configuration object.
20415  */
20416 Roo.data.ScriptTagProxy = function(config){
20417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20418     Roo.apply(this, config);
20419     this.head = document.getElementsByTagName("head")[0];
20420 };
20421
20422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20423
20424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20425     /**
20426      * @cfg {String} url The URL from which to request the data object.
20427      */
20428     /**
20429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20430      */
20431     timeout : 30000,
20432     /**
20433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20434      * the server the name of the callback function set up by the load call to process the returned data object.
20435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20436      * javascript output which calls this named function passing the data object as its only parameter.
20437      */
20438     callbackParam : "callback",
20439     /**
20440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20441      * name to the request.
20442      */
20443     nocache : true,
20444
20445     /**
20446      * Load data from the configured URL, read the data object into
20447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20448      * process that block using the passed callback.
20449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20450      * for the request to the remote server.
20451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20452      * object into a block of Roo.data.Records.
20453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20454      * The function must be passed <ul>
20455      * <li>The Record block object</li>
20456      * <li>The "arg" argument from the load function</li>
20457      * <li>A boolean success indicator</li>
20458      * </ul>
20459      * @param {Object} scope The scope in which to call the callback
20460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20461      */
20462     load : function(params, reader, callback, scope, arg){
20463         if(this.fireEvent("beforeload", this, params) !== false){
20464
20465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20466
20467             var url = this.url;
20468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20469             if(this.nocache){
20470                 url += "&_dc=" + (new Date().getTime());
20471             }
20472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20473             var trans = {
20474                 id : transId,
20475                 cb : "stcCallback"+transId,
20476                 scriptId : "stcScript"+transId,
20477                 params : params,
20478                 arg : arg,
20479                 url : url,
20480                 callback : callback,
20481                 scope : scope,
20482                 reader : reader
20483             };
20484             var conn = this;
20485
20486             window[trans.cb] = function(o){
20487                 conn.handleResponse(o, trans);
20488             };
20489
20490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20491
20492             if(this.autoAbort !== false){
20493                 this.abort();
20494             }
20495
20496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20497
20498             var script = document.createElement("script");
20499             script.setAttribute("src", url);
20500             script.setAttribute("type", "text/javascript");
20501             script.setAttribute("id", trans.scriptId);
20502             this.head.appendChild(script);
20503
20504             this.trans = trans;
20505         }else{
20506             callback.call(scope||this, null, arg, false);
20507         }
20508     },
20509
20510     // private
20511     isLoading : function(){
20512         return this.trans ? true : false;
20513     },
20514
20515     /**
20516      * Abort the current server request.
20517      */
20518     abort : function(){
20519         if(this.isLoading()){
20520             this.destroyTrans(this.trans);
20521         }
20522     },
20523
20524     // private
20525     destroyTrans : function(trans, isLoaded){
20526         this.head.removeChild(document.getElementById(trans.scriptId));
20527         clearTimeout(trans.timeoutId);
20528         if(isLoaded){
20529             window[trans.cb] = undefined;
20530             try{
20531                 delete window[trans.cb];
20532             }catch(e){}
20533         }else{
20534             // if hasn't been loaded, wait for load to remove it to prevent script error
20535             window[trans.cb] = function(){
20536                 window[trans.cb] = undefined;
20537                 try{
20538                     delete window[trans.cb];
20539                 }catch(e){}
20540             };
20541         }
20542     },
20543
20544     // private
20545     handleResponse : function(o, trans){
20546         this.trans = false;
20547         this.destroyTrans(trans, true);
20548         var result;
20549         try {
20550             result = trans.reader.readRecords(o);
20551         }catch(e){
20552             this.fireEvent("loadexception", this, o, trans.arg, e);
20553             trans.callback.call(trans.scope||window, null, trans.arg, false);
20554             return;
20555         }
20556         this.fireEvent("load", this, o, trans.arg);
20557         trans.callback.call(trans.scope||window, result, trans.arg, true);
20558     },
20559
20560     // private
20561     handleFailure : function(trans){
20562         this.trans = false;
20563         this.destroyTrans(trans, false);
20564         this.fireEvent("loadexception", this, null, trans.arg);
20565         trans.callback.call(trans.scope||window, null, trans.arg, false);
20566     }
20567 });/*
20568  * Based on:
20569  * Ext JS Library 1.1.1
20570  * Copyright(c) 2006-2007, Ext JS, LLC.
20571  *
20572  * Originally Released Under LGPL - original licence link has changed is not relivant.
20573  *
20574  * Fork - LGPL
20575  * <script type="text/javascript">
20576  */
20577
20578 /**
20579  * @class Roo.data.JsonReader
20580  * @extends Roo.data.DataReader
20581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20582  * based on mappings in a provided Roo.data.Record constructor.
20583  * 
20584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20585  * in the reply previously. 
20586  * 
20587  * <p>
20588  * Example code:
20589  * <pre><code>
20590 var RecordDef = Roo.data.Record.create([
20591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20593 ]);
20594 var myReader = new Roo.data.JsonReader({
20595     totalProperty: "results",    // The property which contains the total dataset size (optional)
20596     root: "rows",                // The property which contains an Array of row objects
20597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20598 }, RecordDef);
20599 </code></pre>
20600  * <p>
20601  * This would consume a JSON file like this:
20602  * <pre><code>
20603 { 'results': 2, 'rows': [
20604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20606 }
20607 </code></pre>
20608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20610  * paged from the remote server.
20611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20612  * @cfg {String} root name of the property which contains the Array of row objects.
20613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20614  * @constructor
20615  * Create a new JsonReader
20616  * @param {Object} meta Metadata configuration options
20617  * @param {Object} recordType Either an Array of field definition objects,
20618  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20619  */
20620 Roo.data.JsonReader = function(meta, recordType){
20621     
20622     meta = meta || {};
20623     // set some defaults:
20624     Roo.applyIf(meta, {
20625         totalProperty: 'total',
20626         successProperty : 'success',
20627         root : 'data',
20628         id : 'id'
20629     });
20630     
20631     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20632 };
20633 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20634     
20635     /**
20636      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20637      * Used by Store query builder to append _requestMeta to params.
20638      * 
20639      */
20640     metaFromRemote : false,
20641     /**
20642      * This method is only used by a DataProxy which has retrieved data from a remote server.
20643      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20644      * @return {Object} data A data block which is used by an Roo.data.Store object as
20645      * a cache of Roo.data.Records.
20646      */
20647     read : function(response){
20648         var json = response.responseText;
20649        
20650         var o = /* eval:var:o */ eval("("+json+")");
20651         if(!o) {
20652             throw {message: "JsonReader.read: Json object not found"};
20653         }
20654         
20655         if(o.metaData){
20656             
20657             delete this.ef;
20658             this.metaFromRemote = true;
20659             this.meta = o.metaData;
20660             this.recordType = Roo.data.Record.create(o.metaData.fields);
20661             this.onMetaChange(this.meta, this.recordType, o);
20662         }
20663         return this.readRecords(o);
20664     },
20665
20666     // private function a store will implement
20667     onMetaChange : function(meta, recordType, o){
20668
20669     },
20670
20671     /**
20672          * @ignore
20673          */
20674     simpleAccess: function(obj, subsc) {
20675         return obj[subsc];
20676     },
20677
20678         /**
20679          * @ignore
20680          */
20681     getJsonAccessor: function(){
20682         var re = /[\[\.]/;
20683         return function(expr) {
20684             try {
20685                 return(re.test(expr))
20686                     ? new Function("obj", "return obj." + expr)
20687                     : function(obj){
20688                         return obj[expr];
20689                     };
20690             } catch(e){}
20691             return Roo.emptyFn;
20692         };
20693     }(),
20694
20695     /**
20696      * Create a data block containing Roo.data.Records from an XML document.
20697      * @param {Object} o An object which contains an Array of row objects in the property specified
20698      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20699      * which contains the total size of the dataset.
20700      * @return {Object} data A data block which is used by an Roo.data.Store object as
20701      * a cache of Roo.data.Records.
20702      */
20703     readRecords : function(o){
20704         /**
20705          * After any data loads, the raw JSON data is available for further custom processing.
20706          * @type Object
20707          */
20708         this.jsonData = o;
20709         var s = this.meta, Record = this.recordType,
20710             f = Record.prototype.fields, fi = f.items, fl = f.length;
20711
20712 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20713         if (!this.ef) {
20714             if(s.totalProperty) {
20715                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20716                 }
20717                 if(s.successProperty) {
20718                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20719                 }
20720                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20721                 if (s.id) {
20722                         var g = this.getJsonAccessor(s.id);
20723                         this.getId = function(rec) {
20724                                 var r = g(rec);
20725                                 return (r === undefined || r === "") ? null : r;
20726                         };
20727                 } else {
20728                         this.getId = function(){return null;};
20729                 }
20730             this.ef = [];
20731             for(var jj = 0; jj < fl; jj++){
20732                 f = fi[jj];
20733                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20734                 this.ef[jj] = this.getJsonAccessor(map);
20735             }
20736         }
20737
20738         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20739         if(s.totalProperty){
20740             var vt = parseInt(this.getTotal(o), 10);
20741             if(!isNaN(vt)){
20742                 totalRecords = vt;
20743             }
20744         }
20745         if(s.successProperty){
20746             var vs = this.getSuccess(o);
20747             if(vs === false || vs === 'false'){
20748                 success = false;
20749             }
20750         }
20751         var records = [];
20752             for(var i = 0; i < c; i++){
20753                     var n = root[i];
20754                 var values = {};
20755                 var id = this.getId(n);
20756                 for(var j = 0; j < fl; j++){
20757                     f = fi[j];
20758                 var v = this.ef[j](n);
20759                 if (!f.convert) {
20760                     Roo.log('missing convert for ' + f.name);
20761                     Roo.log(f);
20762                     continue;
20763                 }
20764                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20765                 }
20766                 var record = new Record(values, id);
20767                 record.json = n;
20768                 records[i] = record;
20769             }
20770             return {
20771                 success : success,
20772                 records : records,
20773                 totalRecords : totalRecords
20774             };
20775     }
20776 });/*
20777  * Based on:
20778  * Ext JS Library 1.1.1
20779  * Copyright(c) 2006-2007, Ext JS, LLC.
20780  *
20781  * Originally Released Under LGPL - original licence link has changed is not relivant.
20782  *
20783  * Fork - LGPL
20784  * <script type="text/javascript">
20785  */
20786
20787 /**
20788  * @class Roo.data.XmlReader
20789  * @extends Roo.data.DataReader
20790  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20791  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20792  * <p>
20793  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20794  * header in the HTTP response must be set to "text/xml".</em>
20795  * <p>
20796  * Example code:
20797  * <pre><code>
20798 var RecordDef = Roo.data.Record.create([
20799    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20800    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20801 ]);
20802 var myReader = new Roo.data.XmlReader({
20803    totalRecords: "results", // The element which contains the total dataset size (optional)
20804    record: "row",           // The repeated element which contains row information
20805    id: "id"                 // The element within the row that provides an ID for the record (optional)
20806 }, RecordDef);
20807 </code></pre>
20808  * <p>
20809  * This would consume an XML file like this:
20810  * <pre><code>
20811 &lt;?xml?>
20812 &lt;dataset>
20813  &lt;results>2&lt;/results>
20814  &lt;row>
20815    &lt;id>1&lt;/id>
20816    &lt;name>Bill&lt;/name>
20817    &lt;occupation>Gardener&lt;/occupation>
20818  &lt;/row>
20819  &lt;row>
20820    &lt;id>2&lt;/id>
20821    &lt;name>Ben&lt;/name>
20822    &lt;occupation>Horticulturalist&lt;/occupation>
20823  &lt;/row>
20824 &lt;/dataset>
20825 </code></pre>
20826  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20827  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20828  * paged from the remote server.
20829  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20830  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20831  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20832  * a record identifier value.
20833  * @constructor
20834  * Create a new XmlReader
20835  * @param {Object} meta Metadata configuration options
20836  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20837  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20838  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20839  */
20840 Roo.data.XmlReader = function(meta, recordType){
20841     meta = meta || {};
20842     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20843 };
20844 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20845     /**
20846      * This method is only used by a DataProxy which has retrieved data from a remote server.
20847          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20848          * to contain a method called 'responseXML' that returns an XML document object.
20849      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20850      * a cache of Roo.data.Records.
20851      */
20852     read : function(response){
20853         var doc = response.responseXML;
20854         if(!doc) {
20855             throw {message: "XmlReader.read: XML Document not available"};
20856         }
20857         return this.readRecords(doc);
20858     },
20859
20860     /**
20861      * Create a data block containing Roo.data.Records from an XML document.
20862          * @param {Object} doc A parsed XML document.
20863      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20864      * a cache of Roo.data.Records.
20865      */
20866     readRecords : function(doc){
20867         /**
20868          * After any data loads/reads, the raw XML Document is available for further custom processing.
20869          * @type XMLDocument
20870          */
20871         this.xmlData = doc;
20872         var root = doc.documentElement || doc;
20873         var q = Roo.DomQuery;
20874         var recordType = this.recordType, fields = recordType.prototype.fields;
20875         var sid = this.meta.id;
20876         var totalRecords = 0, success = true;
20877         if(this.meta.totalRecords){
20878             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20879         }
20880         
20881         if(this.meta.success){
20882             var sv = q.selectValue(this.meta.success, root, true);
20883             success = sv !== false && sv !== 'false';
20884         }
20885         var records = [];
20886         var ns = q.select(this.meta.record, root);
20887         for(var i = 0, len = ns.length; i < len; i++) {
20888                 var n = ns[i];
20889                 var values = {};
20890                 var id = sid ? q.selectValue(sid, n) : undefined;
20891                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20892                     var f = fields.items[j];
20893                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20894                     v = f.convert(v);
20895                     values[f.name] = v;
20896                 }
20897                 var record = new recordType(values, id);
20898                 record.node = n;
20899                 records[records.length] = record;
20900             }
20901
20902             return {
20903                 success : success,
20904                 records : records,
20905                 totalRecords : totalRecords || records.length
20906             };
20907     }
20908 });/*
20909  * Based on:
20910  * Ext JS Library 1.1.1
20911  * Copyright(c) 2006-2007, Ext JS, LLC.
20912  *
20913  * Originally Released Under LGPL - original licence link has changed is not relivant.
20914  *
20915  * Fork - LGPL
20916  * <script type="text/javascript">
20917  */
20918
20919 /**
20920  * @class Roo.data.ArrayReader
20921  * @extends Roo.data.DataReader
20922  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20923  * Each element of that Array represents a row of data fields. The
20924  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20925  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20926  * <p>
20927  * Example code:.
20928  * <pre><code>
20929 var RecordDef = Roo.data.Record.create([
20930     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20931     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20932 ]);
20933 var myReader = new Roo.data.ArrayReader({
20934     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20935 }, RecordDef);
20936 </code></pre>
20937  * <p>
20938  * This would consume an Array like this:
20939  * <pre><code>
20940 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20941   </code></pre>
20942  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20943  * @constructor
20944  * Create a new JsonReader
20945  * @param {Object} meta Metadata configuration options.
20946  * @param {Object} recordType Either an Array of field definition objects
20947  * as specified to {@link Roo.data.Record#create},
20948  * or an {@link Roo.data.Record} object
20949  * created using {@link Roo.data.Record#create}.
20950  */
20951 Roo.data.ArrayReader = function(meta, recordType){
20952     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20953 };
20954
20955 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20956     /**
20957      * Create a data block containing Roo.data.Records from an XML document.
20958      * @param {Object} o An Array of row objects which represents the dataset.
20959      * @return {Object} data A data block which is used by an Roo.data.Store object as
20960      * a cache of Roo.data.Records.
20961      */
20962     readRecords : function(o){
20963         var sid = this.meta ? this.meta.id : null;
20964         var recordType = this.recordType, fields = recordType.prototype.fields;
20965         var records = [];
20966         var root = o;
20967             for(var i = 0; i < root.length; i++){
20968                     var n = root[i];
20969                 var values = {};
20970                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20971                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20972                 var f = fields.items[j];
20973                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20974                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20975                 v = f.convert(v);
20976                 values[f.name] = v;
20977             }
20978                 var record = new recordType(values, id);
20979                 record.json = n;
20980                 records[records.length] = record;
20981             }
20982             return {
20983                 records : records,
20984                 totalRecords : records.length
20985             };
20986     }
20987 });/*
20988  * Based on:
20989  * Ext JS Library 1.1.1
20990  * Copyright(c) 2006-2007, Ext JS, LLC.
20991  *
20992  * Originally Released Under LGPL - original licence link has changed is not relivant.
20993  *
20994  * Fork - LGPL
20995  * <script type="text/javascript">
20996  */
20997
20998
20999 /**
21000  * @class Roo.data.Tree
21001  * @extends Roo.util.Observable
21002  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21003  * in the tree have most standard DOM functionality.
21004  * @constructor
21005  * @param {Node} root (optional) The root node
21006  */
21007 Roo.data.Tree = function(root){
21008    this.nodeHash = {};
21009    /**
21010     * The root node for this tree
21011     * @type Node
21012     */
21013    this.root = null;
21014    if(root){
21015        this.setRootNode(root);
21016    }
21017    this.addEvents({
21018        /**
21019         * @event append
21020         * Fires when a new child node is appended to a node in this tree.
21021         * @param {Tree} tree The owner tree
21022         * @param {Node} parent The parent node
21023         * @param {Node} node The newly appended node
21024         * @param {Number} index The index of the newly appended node
21025         */
21026        "append" : true,
21027        /**
21028         * @event remove
21029         * Fires when a child node is removed from a node in this tree.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The child node removed
21033         */
21034        "remove" : true,
21035        /**
21036         * @event move
21037         * Fires when a node is moved to a new location in the tree
21038         * @param {Tree} tree The owner tree
21039         * @param {Node} node The node moved
21040         * @param {Node} oldParent The old parent of this node
21041         * @param {Node} newParent The new parent of this node
21042         * @param {Number} index The index it was moved to
21043         */
21044        "move" : true,
21045        /**
21046         * @event insert
21047         * Fires when a new child node is inserted in a node in this tree.
21048         * @param {Tree} tree The owner tree
21049         * @param {Node} parent The parent node
21050         * @param {Node} node The child node inserted
21051         * @param {Node} refNode The child node the node was inserted before
21052         */
21053        "insert" : true,
21054        /**
21055         * @event beforeappend
21056         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21057         * @param {Tree} tree The owner tree
21058         * @param {Node} parent The parent node
21059         * @param {Node} node The child node to be appended
21060         */
21061        "beforeappend" : true,
21062        /**
21063         * @event beforeremove
21064         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21065         * @param {Tree} tree The owner tree
21066         * @param {Node} parent The parent node
21067         * @param {Node} node The child node to be removed
21068         */
21069        "beforeremove" : true,
21070        /**
21071         * @event beforemove
21072         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21073         * @param {Tree} tree The owner tree
21074         * @param {Node} node The node being moved
21075         * @param {Node} oldParent The parent of the node
21076         * @param {Node} newParent The new parent the node is moving to
21077         * @param {Number} index The index it is being moved to
21078         */
21079        "beforemove" : true,
21080        /**
21081         * @event beforeinsert
21082         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21083         * @param {Tree} tree The owner tree
21084         * @param {Node} parent The parent node
21085         * @param {Node} node The child node to be inserted
21086         * @param {Node} refNode The child node the node is being inserted before
21087         */
21088        "beforeinsert" : true
21089    });
21090
21091     Roo.data.Tree.superclass.constructor.call(this);
21092 };
21093
21094 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21095     pathSeparator: "/",
21096
21097     proxyNodeEvent : function(){
21098         return this.fireEvent.apply(this, arguments);
21099     },
21100
21101     /**
21102      * Returns the root node for this tree.
21103      * @return {Node}
21104      */
21105     getRootNode : function(){
21106         return this.root;
21107     },
21108
21109     /**
21110      * Sets the root node for this tree.
21111      * @param {Node} node
21112      * @return {Node}
21113      */
21114     setRootNode : function(node){
21115         this.root = node;
21116         node.ownerTree = this;
21117         node.isRoot = true;
21118         this.registerNode(node);
21119         return node;
21120     },
21121
21122     /**
21123      * Gets a node in this tree by its id.
21124      * @param {String} id
21125      * @return {Node}
21126      */
21127     getNodeById : function(id){
21128         return this.nodeHash[id];
21129     },
21130
21131     registerNode : function(node){
21132         this.nodeHash[node.id] = node;
21133     },
21134
21135     unregisterNode : function(node){
21136         delete this.nodeHash[node.id];
21137     },
21138
21139     toString : function(){
21140         return "[Tree"+(this.id?" "+this.id:"")+"]";
21141     }
21142 });
21143
21144 /**
21145  * @class Roo.data.Node
21146  * @extends Roo.util.Observable
21147  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21148  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21149  * @constructor
21150  * @param {Object} attributes The attributes/config for the node
21151  */
21152 Roo.data.Node = function(attributes){
21153     /**
21154      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21155      * @type {Object}
21156      */
21157     this.attributes = attributes || {};
21158     this.leaf = this.attributes.leaf;
21159     /**
21160      * The node id. @type String
21161      */
21162     this.id = this.attributes.id;
21163     if(!this.id){
21164         this.id = Roo.id(null, "ynode-");
21165         this.attributes.id = this.id;
21166     }
21167     /**
21168      * All child nodes of this node. @type Array
21169      */
21170     this.childNodes = [];
21171     if(!this.childNodes.indexOf){ // indexOf is a must
21172         this.childNodes.indexOf = function(o){
21173             for(var i = 0, len = this.length; i < len; i++){
21174                 if(this[i] == o) {
21175                     return i;
21176                 }
21177             }
21178             return -1;
21179         };
21180     }
21181     /**
21182      * The parent node for this node. @type Node
21183      */
21184     this.parentNode = null;
21185     /**
21186      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21187      */
21188     this.firstChild = null;
21189     /**
21190      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21191      */
21192     this.lastChild = null;
21193     /**
21194      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21195      */
21196     this.previousSibling = null;
21197     /**
21198      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21199      */
21200     this.nextSibling = null;
21201
21202     this.addEvents({
21203        /**
21204         * @event append
21205         * Fires when a new child node is appended
21206         * @param {Tree} tree The owner tree
21207         * @param {Node} this This node
21208         * @param {Node} node The newly appended node
21209         * @param {Number} index The index of the newly appended node
21210         */
21211        "append" : true,
21212        /**
21213         * @event remove
21214         * Fires when a child node is removed
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The removed node
21218         */
21219        "remove" : true,
21220        /**
21221         * @event move
21222         * Fires when this node is moved to a new location in the tree
21223         * @param {Tree} tree The owner tree
21224         * @param {Node} this This node
21225         * @param {Node} oldParent The old parent of this node
21226         * @param {Node} newParent The new parent of this node
21227         * @param {Number} index The index it was moved to
21228         */
21229        "move" : true,
21230        /**
21231         * @event insert
21232         * Fires when a new child node is inserted.
21233         * @param {Tree} tree The owner tree
21234         * @param {Node} this This node
21235         * @param {Node} node The child node inserted
21236         * @param {Node} refNode The child node the node was inserted before
21237         */
21238        "insert" : true,
21239        /**
21240         * @event beforeappend
21241         * Fires before a new child is appended, return false to cancel the append.
21242         * @param {Tree} tree The owner tree
21243         * @param {Node} this This node
21244         * @param {Node} node The child node to be appended
21245         */
21246        "beforeappend" : true,
21247        /**
21248         * @event beforeremove
21249         * Fires before a child is removed, return false to cancel the remove.
21250         * @param {Tree} tree The owner tree
21251         * @param {Node} this This node
21252         * @param {Node} node The child node to be removed
21253         */
21254        "beforeremove" : true,
21255        /**
21256         * @event beforemove
21257         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21258         * @param {Tree} tree The owner tree
21259         * @param {Node} this This node
21260         * @param {Node} oldParent The parent of this node
21261         * @param {Node} newParent The new parent this node is moving to
21262         * @param {Number} index The index it is being moved to
21263         */
21264        "beforemove" : true,
21265        /**
21266         * @event beforeinsert
21267         * Fires before a new child is inserted, return false to cancel the insert.
21268         * @param {Tree} tree The owner tree
21269         * @param {Node} this This node
21270         * @param {Node} node The child node to be inserted
21271         * @param {Node} refNode The child node the node is being inserted before
21272         */
21273        "beforeinsert" : true
21274    });
21275     this.listeners = this.attributes.listeners;
21276     Roo.data.Node.superclass.constructor.call(this);
21277 };
21278
21279 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21280     fireEvent : function(evtName){
21281         // first do standard event for this node
21282         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21283             return false;
21284         }
21285         // then bubble it up to the tree if the event wasn't cancelled
21286         var ot = this.getOwnerTree();
21287         if(ot){
21288             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21289                 return false;
21290             }
21291         }
21292         return true;
21293     },
21294
21295     /**
21296      * Returns true if this node is a leaf
21297      * @return {Boolean}
21298      */
21299     isLeaf : function(){
21300         return this.leaf === true;
21301     },
21302
21303     // private
21304     setFirstChild : function(node){
21305         this.firstChild = node;
21306     },
21307
21308     //private
21309     setLastChild : function(node){
21310         this.lastChild = node;
21311     },
21312
21313
21314     /**
21315      * Returns true if this node is the last child of its parent
21316      * @return {Boolean}
21317      */
21318     isLast : function(){
21319        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21320     },
21321
21322     /**
21323      * Returns true if this node is the first child of its parent
21324      * @return {Boolean}
21325      */
21326     isFirst : function(){
21327        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21328     },
21329
21330     hasChildNodes : function(){
21331         return !this.isLeaf() && this.childNodes.length > 0;
21332     },
21333
21334     /**
21335      * Insert node(s) as the last child node of this node.
21336      * @param {Node/Array} node The node or Array of nodes to append
21337      * @return {Node} The appended node if single append, or null if an array was passed
21338      */
21339     appendChild : function(node){
21340         var multi = false;
21341         if(node instanceof Array){
21342             multi = node;
21343         }else if(arguments.length > 1){
21344             multi = arguments;
21345         }
21346         // if passed an array or multiple args do them one by one
21347         if(multi){
21348             for(var i = 0, len = multi.length; i < len; i++) {
21349                 this.appendChild(multi[i]);
21350             }
21351         }else{
21352             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21353                 return false;
21354             }
21355             var index = this.childNodes.length;
21356             var oldParent = node.parentNode;
21357             // it's a move, make sure we move it cleanly
21358             if(oldParent){
21359                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21360                     return false;
21361                 }
21362                 oldParent.removeChild(node);
21363             }
21364             index = this.childNodes.length;
21365             if(index == 0){
21366                 this.setFirstChild(node);
21367             }
21368             this.childNodes.push(node);
21369             node.parentNode = this;
21370             var ps = this.childNodes[index-1];
21371             if(ps){
21372                 node.previousSibling = ps;
21373                 ps.nextSibling = node;
21374             }else{
21375                 node.previousSibling = null;
21376             }
21377             node.nextSibling = null;
21378             this.setLastChild(node);
21379             node.setOwnerTree(this.getOwnerTree());
21380             this.fireEvent("append", this.ownerTree, this, node, index);
21381             if(oldParent){
21382                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21383             }
21384             return node;
21385         }
21386     },
21387
21388     /**
21389      * Removes a child node from this node.
21390      * @param {Node} node The node to remove
21391      * @return {Node} The removed node
21392      */
21393     removeChild : function(node){
21394         var index = this.childNodes.indexOf(node);
21395         if(index == -1){
21396             return false;
21397         }
21398         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21399             return false;
21400         }
21401
21402         // remove it from childNodes collection
21403         this.childNodes.splice(index, 1);
21404
21405         // update siblings
21406         if(node.previousSibling){
21407             node.previousSibling.nextSibling = node.nextSibling;
21408         }
21409         if(node.nextSibling){
21410             node.nextSibling.previousSibling = node.previousSibling;
21411         }
21412
21413         // update child refs
21414         if(this.firstChild == node){
21415             this.setFirstChild(node.nextSibling);
21416         }
21417         if(this.lastChild == node){
21418             this.setLastChild(node.previousSibling);
21419         }
21420
21421         node.setOwnerTree(null);
21422         // clear any references from the node
21423         node.parentNode = null;
21424         node.previousSibling = null;
21425         node.nextSibling = null;
21426         this.fireEvent("remove", this.ownerTree, this, node);
21427         return node;
21428     },
21429
21430     /**
21431      * Inserts the first node before the second node in this nodes childNodes collection.
21432      * @param {Node} node The node to insert
21433      * @param {Node} refNode The node to insert before (if null the node is appended)
21434      * @return {Node} The inserted node
21435      */
21436     insertBefore : function(node, refNode){
21437         if(!refNode){ // like standard Dom, refNode can be null for append
21438             return this.appendChild(node);
21439         }
21440         // nothing to do
21441         if(node == refNode){
21442             return false;
21443         }
21444
21445         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21446             return false;
21447         }
21448         var index = this.childNodes.indexOf(refNode);
21449         var oldParent = node.parentNode;
21450         var refIndex = index;
21451
21452         // when moving internally, indexes will change after remove
21453         if(oldParent == this && this.childNodes.indexOf(node) < index){
21454             refIndex--;
21455         }
21456
21457         // it's a move, make sure we move it cleanly
21458         if(oldParent){
21459             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21460                 return false;
21461             }
21462             oldParent.removeChild(node);
21463         }
21464         if(refIndex == 0){
21465             this.setFirstChild(node);
21466         }
21467         this.childNodes.splice(refIndex, 0, node);
21468         node.parentNode = this;
21469         var ps = this.childNodes[refIndex-1];
21470         if(ps){
21471             node.previousSibling = ps;
21472             ps.nextSibling = node;
21473         }else{
21474             node.previousSibling = null;
21475         }
21476         node.nextSibling = refNode;
21477         refNode.previousSibling = node;
21478         node.setOwnerTree(this.getOwnerTree());
21479         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21480         if(oldParent){
21481             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21482         }
21483         return node;
21484     },
21485
21486     /**
21487      * Returns the child node at the specified index.
21488      * @param {Number} index
21489      * @return {Node}
21490      */
21491     item : function(index){
21492         return this.childNodes[index];
21493     },
21494
21495     /**
21496      * Replaces one child node in this node with another.
21497      * @param {Node} newChild The replacement node
21498      * @param {Node} oldChild The node to replace
21499      * @return {Node} The replaced node
21500      */
21501     replaceChild : function(newChild, oldChild){
21502         this.insertBefore(newChild, oldChild);
21503         this.removeChild(oldChild);
21504         return oldChild;
21505     },
21506
21507     /**
21508      * Returns the index of a child node
21509      * @param {Node} node
21510      * @return {Number} The index of the node or -1 if it was not found
21511      */
21512     indexOf : function(child){
21513         return this.childNodes.indexOf(child);
21514     },
21515
21516     /**
21517      * Returns the tree this node is in.
21518      * @return {Tree}
21519      */
21520     getOwnerTree : function(){
21521         // if it doesn't have one, look for one
21522         if(!this.ownerTree){
21523             var p = this;
21524             while(p){
21525                 if(p.ownerTree){
21526                     this.ownerTree = p.ownerTree;
21527                     break;
21528                 }
21529                 p = p.parentNode;
21530             }
21531         }
21532         return this.ownerTree;
21533     },
21534
21535     /**
21536      * Returns depth of this node (the root node has a depth of 0)
21537      * @return {Number}
21538      */
21539     getDepth : function(){
21540         var depth = 0;
21541         var p = this;
21542         while(p.parentNode){
21543             ++depth;
21544             p = p.parentNode;
21545         }
21546         return depth;
21547     },
21548
21549     // private
21550     setOwnerTree : function(tree){
21551         // if it's move, we need to update everyone
21552         if(tree != this.ownerTree){
21553             if(this.ownerTree){
21554                 this.ownerTree.unregisterNode(this);
21555             }
21556             this.ownerTree = tree;
21557             var cs = this.childNodes;
21558             for(var i = 0, len = cs.length; i < len; i++) {
21559                 cs[i].setOwnerTree(tree);
21560             }
21561             if(tree){
21562                 tree.registerNode(this);
21563             }
21564         }
21565     },
21566
21567     /**
21568      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21569      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21570      * @return {String} The path
21571      */
21572     getPath : function(attr){
21573         attr = attr || "id";
21574         var p = this.parentNode;
21575         var b = [this.attributes[attr]];
21576         while(p){
21577             b.unshift(p.attributes[attr]);
21578             p = p.parentNode;
21579         }
21580         var sep = this.getOwnerTree().pathSeparator;
21581         return sep + b.join(sep);
21582     },
21583
21584     /**
21585      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21586      * function call will be the scope provided or the current node. The arguments to the function
21587      * will be the args provided or the current node. If the function returns false at any point,
21588      * the bubble is stopped.
21589      * @param {Function} fn The function to call
21590      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21591      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21592      */
21593     bubble : function(fn, scope, args){
21594         var p = this;
21595         while(p){
21596             if(fn.call(scope || p, args || p) === false){
21597                 break;
21598             }
21599             p = p.parentNode;
21600         }
21601     },
21602
21603     /**
21604      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21605      * function call will be the scope provided or the current node. The arguments to the function
21606      * will be the args provided or the current node. If the function returns false at any point,
21607      * the cascade is stopped on that branch.
21608      * @param {Function} fn The function to call
21609      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21610      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21611      */
21612     cascade : function(fn, scope, args){
21613         if(fn.call(scope || this, args || this) !== false){
21614             var cs = this.childNodes;
21615             for(var i = 0, len = cs.length; i < len; i++) {
21616                 cs[i].cascade(fn, scope, args);
21617             }
21618         }
21619     },
21620
21621     /**
21622      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21623      * function call will be the scope provided or the current node. The arguments to the function
21624      * will be the args provided or the current node. If the function returns false at any point,
21625      * the iteration stops.
21626      * @param {Function} fn The function to call
21627      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21628      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21629      */
21630     eachChild : function(fn, scope, args){
21631         var cs = this.childNodes;
21632         for(var i = 0, len = cs.length; i < len; i++) {
21633                 if(fn.call(scope || this, args || cs[i]) === false){
21634                     break;
21635                 }
21636         }
21637     },
21638
21639     /**
21640      * Finds the first child that has the attribute with the specified value.
21641      * @param {String} attribute The attribute name
21642      * @param {Mixed} value The value to search for
21643      * @return {Node} The found child or null if none was found
21644      */
21645     findChild : function(attribute, value){
21646         var cs = this.childNodes;
21647         for(var i = 0, len = cs.length; i < len; i++) {
21648                 if(cs[i].attributes[attribute] == value){
21649                     return cs[i];
21650                 }
21651         }
21652         return null;
21653     },
21654
21655     /**
21656      * Finds the first child by a custom function. The child matches if the function passed
21657      * returns true.
21658      * @param {Function} fn
21659      * @param {Object} scope (optional)
21660      * @return {Node} The found child or null if none was found
21661      */
21662     findChildBy : function(fn, scope){
21663         var cs = this.childNodes;
21664         for(var i = 0, len = cs.length; i < len; i++) {
21665                 if(fn.call(scope||cs[i], cs[i]) === true){
21666                     return cs[i];
21667                 }
21668         }
21669         return null;
21670     },
21671
21672     /**
21673      * Sorts this nodes children using the supplied sort function
21674      * @param {Function} fn
21675      * @param {Object} scope (optional)
21676      */
21677     sort : function(fn, scope){
21678         var cs = this.childNodes;
21679         var len = cs.length;
21680         if(len > 0){
21681             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21682             cs.sort(sortFn);
21683             for(var i = 0; i < len; i++){
21684                 var n = cs[i];
21685                 n.previousSibling = cs[i-1];
21686                 n.nextSibling = cs[i+1];
21687                 if(i == 0){
21688                     this.setFirstChild(n);
21689                 }
21690                 if(i == len-1){
21691                     this.setLastChild(n);
21692                 }
21693             }
21694         }
21695     },
21696
21697     /**
21698      * Returns true if this node is an ancestor (at any point) of the passed node.
21699      * @param {Node} node
21700      * @return {Boolean}
21701      */
21702     contains : function(node){
21703         return node.isAncestor(this);
21704     },
21705
21706     /**
21707      * Returns true if the passed node is an ancestor (at any point) of this node.
21708      * @param {Node} node
21709      * @return {Boolean}
21710      */
21711     isAncestor : function(node){
21712         var p = this.parentNode;
21713         while(p){
21714             if(p == node){
21715                 return true;
21716             }
21717             p = p.parentNode;
21718         }
21719         return false;
21720     },
21721
21722     toString : function(){
21723         return "[Node"+(this.id?" "+this.id:"")+"]";
21724     }
21725 });/*
21726  * Based on:
21727  * Ext JS Library 1.1.1
21728  * Copyright(c) 2006-2007, Ext JS, LLC.
21729  *
21730  * Originally Released Under LGPL - original licence link has changed is not relivant.
21731  *
21732  * Fork - LGPL
21733  * <script type="text/javascript">
21734  */
21735  
21736
21737 /**
21738  * @class Roo.ComponentMgr
21739  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21740  * @singleton
21741  */
21742 Roo.ComponentMgr = function(){
21743     var all = new Roo.util.MixedCollection();
21744
21745     return {
21746         /**
21747          * Registers a component.
21748          * @param {Roo.Component} c The component
21749          */
21750         register : function(c){
21751             all.add(c);
21752         },
21753
21754         /**
21755          * Unregisters a component.
21756          * @param {Roo.Component} c The component
21757          */
21758         unregister : function(c){
21759             all.remove(c);
21760         },
21761
21762         /**
21763          * Returns a component by id
21764          * @param {String} id The component id
21765          */
21766         get : function(id){
21767             return all.get(id);
21768         },
21769
21770         /**
21771          * Registers a function that will be called when a specified component is added to ComponentMgr
21772          * @param {String} id The component id
21773          * @param {Funtction} fn The callback function
21774          * @param {Object} scope The scope of the callback
21775          */
21776         onAvailable : function(id, fn, scope){
21777             all.on("add", function(index, o){
21778                 if(o.id == id){
21779                     fn.call(scope || o, o);
21780                     all.un("add", fn, scope);
21781                 }
21782             });
21783         }
21784     };
21785 }();/*
21786  * Based on:
21787  * Ext JS Library 1.1.1
21788  * Copyright(c) 2006-2007, Ext JS, LLC.
21789  *
21790  * Originally Released Under LGPL - original licence link has changed is not relivant.
21791  *
21792  * Fork - LGPL
21793  * <script type="text/javascript">
21794  */
21795  
21796 /**
21797  * @class Roo.Component
21798  * @extends Roo.util.Observable
21799  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21800  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21801  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21802  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21803  * All visual components (widgets) that require rendering into a layout should subclass Component.
21804  * @constructor
21805  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21806  * 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
21807  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21808  */
21809 Roo.Component = function(config){
21810     config = config || {};
21811     if(config.tagName || config.dom || typeof config == "string"){ // element object
21812         config = {el: config, id: config.id || config};
21813     }
21814     this.initialConfig = config;
21815
21816     Roo.apply(this, config);
21817     this.addEvents({
21818         /**
21819          * @event disable
21820          * Fires after the component is disabled.
21821              * @param {Roo.Component} this
21822              */
21823         disable : true,
21824         /**
21825          * @event enable
21826          * Fires after the component is enabled.
21827              * @param {Roo.Component} this
21828              */
21829         enable : true,
21830         /**
21831          * @event beforeshow
21832          * Fires before the component is shown.  Return false to stop the show.
21833              * @param {Roo.Component} this
21834              */
21835         beforeshow : true,
21836         /**
21837          * @event show
21838          * Fires after the component is shown.
21839              * @param {Roo.Component} this
21840              */
21841         show : true,
21842         /**
21843          * @event beforehide
21844          * Fires before the component is hidden. Return false to stop the hide.
21845              * @param {Roo.Component} this
21846              */
21847         beforehide : true,
21848         /**
21849          * @event hide
21850          * Fires after the component is hidden.
21851              * @param {Roo.Component} this
21852              */
21853         hide : true,
21854         /**
21855          * @event beforerender
21856          * Fires before the component is rendered. Return false to stop the render.
21857              * @param {Roo.Component} this
21858              */
21859         beforerender : true,
21860         /**
21861          * @event render
21862          * Fires after the component is rendered.
21863              * @param {Roo.Component} this
21864              */
21865         render : true,
21866         /**
21867          * @event beforedestroy
21868          * Fires before the component is destroyed. Return false to stop the destroy.
21869              * @param {Roo.Component} this
21870              */
21871         beforedestroy : true,
21872         /**
21873          * @event destroy
21874          * Fires after the component is destroyed.
21875              * @param {Roo.Component} this
21876              */
21877         destroy : true
21878     });
21879     if(!this.id){
21880         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21881     }
21882     Roo.ComponentMgr.register(this);
21883     Roo.Component.superclass.constructor.call(this);
21884     this.initComponent();
21885     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21886         this.render(this.renderTo);
21887         delete this.renderTo;
21888     }
21889 };
21890
21891 /** @private */
21892 Roo.Component.AUTO_ID = 1000;
21893
21894 Roo.extend(Roo.Component, Roo.util.Observable, {
21895     /**
21896      * @scope Roo.Component.prototype
21897      * @type {Boolean}
21898      * true if this component is hidden. Read-only.
21899      */
21900     hidden : false,
21901     /**
21902      * @type {Boolean}
21903      * true if this component is disabled. Read-only.
21904      */
21905     disabled : false,
21906     /**
21907      * @type {Boolean}
21908      * true if this component has been rendered. Read-only.
21909      */
21910     rendered : false,
21911     
21912     /** @cfg {String} disableClass
21913      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21914      */
21915     disabledClass : "x-item-disabled",
21916         /** @cfg {Boolean} allowDomMove
21917          * Whether the component can move the Dom node when rendering (defaults to true).
21918          */
21919     allowDomMove : true,
21920     /** @cfg {String} hideMode
21921      * How this component should hidden. Supported values are
21922      * "visibility" (css visibility), "offsets" (negative offset position) and
21923      * "display" (css display) - defaults to "display".
21924      */
21925     hideMode: 'display',
21926
21927     /** @private */
21928     ctype : "Roo.Component",
21929
21930     /**
21931      * @cfg {String} actionMode 
21932      * which property holds the element that used for  hide() / show() / disable() / enable()
21933      * default is 'el' 
21934      */
21935     actionMode : "el",
21936
21937     /** @private */
21938     getActionEl : function(){
21939         return this[this.actionMode];
21940     },
21941
21942     initComponent : Roo.emptyFn,
21943     /**
21944      * If this is a lazy rendering component, render it to its container element.
21945      * @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.
21946      */
21947     render : function(container, position){
21948         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21949             if(!container && this.el){
21950                 this.el = Roo.get(this.el);
21951                 container = this.el.dom.parentNode;
21952                 this.allowDomMove = false;
21953             }
21954             this.container = Roo.get(container);
21955             this.rendered = true;
21956             if(position !== undefined){
21957                 if(typeof position == 'number'){
21958                     position = this.container.dom.childNodes[position];
21959                 }else{
21960                     position = Roo.getDom(position);
21961                 }
21962             }
21963             this.onRender(this.container, position || null);
21964             if(this.cls){
21965                 this.el.addClass(this.cls);
21966                 delete this.cls;
21967             }
21968             if(this.style){
21969                 this.el.applyStyles(this.style);
21970                 delete this.style;
21971             }
21972             this.fireEvent("render", this);
21973             this.afterRender(this.container);
21974             if(this.hidden){
21975                 this.hide();
21976             }
21977             if(this.disabled){
21978                 this.disable();
21979             }
21980         }
21981         return this;
21982     },
21983
21984     /** @private */
21985     // default function is not really useful
21986     onRender : function(ct, position){
21987         if(this.el){
21988             this.el = Roo.get(this.el);
21989             if(this.allowDomMove !== false){
21990                 ct.dom.insertBefore(this.el.dom, position);
21991             }
21992         }
21993     },
21994
21995     /** @private */
21996     getAutoCreate : function(){
21997         var cfg = typeof this.autoCreate == "object" ?
21998                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21999         if(this.id && !cfg.id){
22000             cfg.id = this.id;
22001         }
22002         return cfg;
22003     },
22004
22005     /** @private */
22006     afterRender : Roo.emptyFn,
22007
22008     /**
22009      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22010      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22011      */
22012     destroy : function(){
22013         if(this.fireEvent("beforedestroy", this) !== false){
22014             this.purgeListeners();
22015             this.beforeDestroy();
22016             if(this.rendered){
22017                 this.el.removeAllListeners();
22018                 this.el.remove();
22019                 if(this.actionMode == "container"){
22020                     this.container.remove();
22021                 }
22022             }
22023             this.onDestroy();
22024             Roo.ComponentMgr.unregister(this);
22025             this.fireEvent("destroy", this);
22026         }
22027     },
22028
22029         /** @private */
22030     beforeDestroy : function(){
22031
22032     },
22033
22034         /** @private */
22035         onDestroy : function(){
22036
22037     },
22038
22039     /**
22040      * Returns the underlying {@link Roo.Element}.
22041      * @return {Roo.Element} The element
22042      */
22043     getEl : function(){
22044         return this.el;
22045     },
22046
22047     /**
22048      * Returns the id of this component.
22049      * @return {String}
22050      */
22051     getId : function(){
22052         return this.id;
22053     },
22054
22055     /**
22056      * Try to focus this component.
22057      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22058      * @return {Roo.Component} this
22059      */
22060     focus : function(selectText){
22061         if(this.rendered){
22062             this.el.focus();
22063             if(selectText === true){
22064                 this.el.dom.select();
22065             }
22066         }
22067         return this;
22068     },
22069
22070     /** @private */
22071     blur : function(){
22072         if(this.rendered){
22073             this.el.blur();
22074         }
22075         return this;
22076     },
22077
22078     /**
22079      * Disable this component.
22080      * @return {Roo.Component} this
22081      */
22082     disable : function(){
22083         if(this.rendered){
22084             this.onDisable();
22085         }
22086         this.disabled = true;
22087         this.fireEvent("disable", this);
22088         return this;
22089     },
22090
22091         // private
22092     onDisable : function(){
22093         this.getActionEl().addClass(this.disabledClass);
22094         this.el.dom.disabled = true;
22095     },
22096
22097     /**
22098      * Enable this component.
22099      * @return {Roo.Component} this
22100      */
22101     enable : function(){
22102         if(this.rendered){
22103             this.onEnable();
22104         }
22105         this.disabled = false;
22106         this.fireEvent("enable", this);
22107         return this;
22108     },
22109
22110         // private
22111     onEnable : function(){
22112         this.getActionEl().removeClass(this.disabledClass);
22113         this.el.dom.disabled = false;
22114     },
22115
22116     /**
22117      * Convenience function for setting disabled/enabled by boolean.
22118      * @param {Boolean} disabled
22119      */
22120     setDisabled : function(disabled){
22121         this[disabled ? "disable" : "enable"]();
22122     },
22123
22124     /**
22125      * Show this component.
22126      * @return {Roo.Component} this
22127      */
22128     show: function(){
22129         if(this.fireEvent("beforeshow", this) !== false){
22130             this.hidden = false;
22131             if(this.rendered){
22132                 this.onShow();
22133             }
22134             this.fireEvent("show", this);
22135         }
22136         return this;
22137     },
22138
22139     // private
22140     onShow : function(){
22141         var ae = this.getActionEl();
22142         if(this.hideMode == 'visibility'){
22143             ae.dom.style.visibility = "visible";
22144         }else if(this.hideMode == 'offsets'){
22145             ae.removeClass('x-hidden');
22146         }else{
22147             ae.dom.style.display = "";
22148         }
22149     },
22150
22151     /**
22152      * Hide this component.
22153      * @return {Roo.Component} this
22154      */
22155     hide: function(){
22156         if(this.fireEvent("beforehide", this) !== false){
22157             this.hidden = true;
22158             if(this.rendered){
22159                 this.onHide();
22160             }
22161             this.fireEvent("hide", this);
22162         }
22163         return this;
22164     },
22165
22166     // private
22167     onHide : function(){
22168         var ae = this.getActionEl();
22169         if(this.hideMode == 'visibility'){
22170             ae.dom.style.visibility = "hidden";
22171         }else if(this.hideMode == 'offsets'){
22172             ae.addClass('x-hidden');
22173         }else{
22174             ae.dom.style.display = "none";
22175         }
22176     },
22177
22178     /**
22179      * Convenience function to hide or show this component by boolean.
22180      * @param {Boolean} visible True to show, false to hide
22181      * @return {Roo.Component} this
22182      */
22183     setVisible: function(visible){
22184         if(visible) {
22185             this.show();
22186         }else{
22187             this.hide();
22188         }
22189         return this;
22190     },
22191
22192     /**
22193      * Returns true if this component is visible.
22194      */
22195     isVisible : function(){
22196         return this.getActionEl().isVisible();
22197     },
22198
22199     cloneConfig : function(overrides){
22200         overrides = overrides || {};
22201         var id = overrides.id || Roo.id();
22202         var cfg = Roo.applyIf(overrides, this.initialConfig);
22203         cfg.id = id; // prevent dup id
22204         return new this.constructor(cfg);
22205     }
22206 });/*
22207  * Based on:
22208  * Ext JS Library 1.1.1
22209  * Copyright(c) 2006-2007, Ext JS, LLC.
22210  *
22211  * Originally Released Under LGPL - original licence link has changed is not relivant.
22212  *
22213  * Fork - LGPL
22214  * <script type="text/javascript">
22215  */
22216  (function(){ 
22217 /**
22218  * @class Roo.Layer
22219  * @extends Roo.Element
22220  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22221  * automatic maintaining of shadow/shim positions.
22222  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22223  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22224  * you can pass a string with a CSS class name. False turns off the shadow.
22225  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22226  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22227  * @cfg {String} cls CSS class to add to the element
22228  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22229  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22230  * @constructor
22231  * @param {Object} config An object with config options.
22232  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22233  */
22234
22235 Roo.Layer = function(config, existingEl){
22236     config = config || {};
22237     var dh = Roo.DomHelper;
22238     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22239     if(existingEl){
22240         this.dom = Roo.getDom(existingEl);
22241     }
22242     if(!this.dom){
22243         var o = config.dh || {tag: "div", cls: "x-layer"};
22244         this.dom = dh.append(pel, o);
22245     }
22246     if(config.cls){
22247         this.addClass(config.cls);
22248     }
22249     this.constrain = config.constrain !== false;
22250     this.visibilityMode = Roo.Element.VISIBILITY;
22251     if(config.id){
22252         this.id = this.dom.id = config.id;
22253     }else{
22254         this.id = Roo.id(this.dom);
22255     }
22256     this.zindex = config.zindex || this.getZIndex();
22257     this.position("absolute", this.zindex);
22258     if(config.shadow){
22259         this.shadowOffset = config.shadowOffset || 4;
22260         this.shadow = new Roo.Shadow({
22261             offset : this.shadowOffset,
22262             mode : config.shadow
22263         });
22264     }else{
22265         this.shadowOffset = 0;
22266     }
22267     this.useShim = config.shim !== false && Roo.useShims;
22268     this.useDisplay = config.useDisplay;
22269     this.hide();
22270 };
22271
22272 var supr = Roo.Element.prototype;
22273
22274 // shims are shared among layer to keep from having 100 iframes
22275 var shims = [];
22276
22277 Roo.extend(Roo.Layer, Roo.Element, {
22278
22279     getZIndex : function(){
22280         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22281     },
22282
22283     getShim : function(){
22284         if(!this.useShim){
22285             return null;
22286         }
22287         if(this.shim){
22288             return this.shim;
22289         }
22290         var shim = shims.shift();
22291         if(!shim){
22292             shim = this.createShim();
22293             shim.enableDisplayMode('block');
22294             shim.dom.style.display = 'none';
22295             shim.dom.style.visibility = 'visible';
22296         }
22297         var pn = this.dom.parentNode;
22298         if(shim.dom.parentNode != pn){
22299             pn.insertBefore(shim.dom, this.dom);
22300         }
22301         shim.setStyle('z-index', this.getZIndex()-2);
22302         this.shim = shim;
22303         return shim;
22304     },
22305
22306     hideShim : function(){
22307         if(this.shim){
22308             this.shim.setDisplayed(false);
22309             shims.push(this.shim);
22310             delete this.shim;
22311         }
22312     },
22313
22314     disableShadow : function(){
22315         if(this.shadow){
22316             this.shadowDisabled = true;
22317             this.shadow.hide();
22318             this.lastShadowOffset = this.shadowOffset;
22319             this.shadowOffset = 0;
22320         }
22321     },
22322
22323     enableShadow : function(show){
22324         if(this.shadow){
22325             this.shadowDisabled = false;
22326             this.shadowOffset = this.lastShadowOffset;
22327             delete this.lastShadowOffset;
22328             if(show){
22329                 this.sync(true);
22330             }
22331         }
22332     },
22333
22334     // private
22335     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22336     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22337     sync : function(doShow){
22338         var sw = this.shadow;
22339         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22340             var sh = this.getShim();
22341
22342             var w = this.getWidth(),
22343                 h = this.getHeight();
22344
22345             var l = this.getLeft(true),
22346                 t = this.getTop(true);
22347
22348             if(sw && !this.shadowDisabled){
22349                 if(doShow && !sw.isVisible()){
22350                     sw.show(this);
22351                 }else{
22352                     sw.realign(l, t, w, h);
22353                 }
22354                 if(sh){
22355                     if(doShow){
22356                        sh.show();
22357                     }
22358                     // fit the shim behind the shadow, so it is shimmed too
22359                     var a = sw.adjusts, s = sh.dom.style;
22360                     s.left = (Math.min(l, l+a.l))+"px";
22361                     s.top = (Math.min(t, t+a.t))+"px";
22362                     s.width = (w+a.w)+"px";
22363                     s.height = (h+a.h)+"px";
22364                 }
22365             }else if(sh){
22366                 if(doShow){
22367                    sh.show();
22368                 }
22369                 sh.setSize(w, h);
22370                 sh.setLeftTop(l, t);
22371             }
22372             
22373         }
22374     },
22375
22376     // private
22377     destroy : function(){
22378         this.hideShim();
22379         if(this.shadow){
22380             this.shadow.hide();
22381         }
22382         this.removeAllListeners();
22383         var pn = this.dom.parentNode;
22384         if(pn){
22385             pn.removeChild(this.dom);
22386         }
22387         Roo.Element.uncache(this.id);
22388     },
22389
22390     remove : function(){
22391         this.destroy();
22392     },
22393
22394     // private
22395     beginUpdate : function(){
22396         this.updating = true;
22397     },
22398
22399     // private
22400     endUpdate : function(){
22401         this.updating = false;
22402         this.sync(true);
22403     },
22404
22405     // private
22406     hideUnders : function(negOffset){
22407         if(this.shadow){
22408             this.shadow.hide();
22409         }
22410         this.hideShim();
22411     },
22412
22413     // private
22414     constrainXY : function(){
22415         if(this.constrain){
22416             var vw = Roo.lib.Dom.getViewWidth(),
22417                 vh = Roo.lib.Dom.getViewHeight();
22418             var s = Roo.get(document).getScroll();
22419
22420             var xy = this.getXY();
22421             var x = xy[0], y = xy[1];   
22422             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22423             // only move it if it needs it
22424             var moved = false;
22425             // first validate right/bottom
22426             if((x + w) > vw+s.left){
22427                 x = vw - w - this.shadowOffset;
22428                 moved = true;
22429             }
22430             if((y + h) > vh+s.top){
22431                 y = vh - h - this.shadowOffset;
22432                 moved = true;
22433             }
22434             // then make sure top/left isn't negative
22435             if(x < s.left){
22436                 x = s.left;
22437                 moved = true;
22438             }
22439             if(y < s.top){
22440                 y = s.top;
22441                 moved = true;
22442             }
22443             if(moved){
22444                 if(this.avoidY){
22445                     var ay = this.avoidY;
22446                     if(y <= ay && (y+h) >= ay){
22447                         y = ay-h-5;   
22448                     }
22449                 }
22450                 xy = [x, y];
22451                 this.storeXY(xy);
22452                 supr.setXY.call(this, xy);
22453                 this.sync();
22454             }
22455         }
22456     },
22457
22458     isVisible : function(){
22459         return this.visible;    
22460     },
22461
22462     // private
22463     showAction : function(){
22464         this.visible = true; // track visibility to prevent getStyle calls
22465         if(this.useDisplay === true){
22466             this.setDisplayed("");
22467         }else if(this.lastXY){
22468             supr.setXY.call(this, this.lastXY);
22469         }else if(this.lastLT){
22470             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22471         }
22472     },
22473
22474     // private
22475     hideAction : function(){
22476         this.visible = false;
22477         if(this.useDisplay === true){
22478             this.setDisplayed(false);
22479         }else{
22480             this.setLeftTop(-10000,-10000);
22481         }
22482     },
22483
22484     // overridden Element method
22485     setVisible : function(v, a, d, c, e){
22486         if(v){
22487             this.showAction();
22488         }
22489         if(a && v){
22490             var cb = function(){
22491                 this.sync(true);
22492                 if(c){
22493                     c();
22494                 }
22495             }.createDelegate(this);
22496             supr.setVisible.call(this, true, true, d, cb, e);
22497         }else{
22498             if(!v){
22499                 this.hideUnders(true);
22500             }
22501             var cb = c;
22502             if(a){
22503                 cb = function(){
22504                     this.hideAction();
22505                     if(c){
22506                         c();
22507                     }
22508                 }.createDelegate(this);
22509             }
22510             supr.setVisible.call(this, v, a, d, cb, e);
22511             if(v){
22512                 this.sync(true);
22513             }else if(!a){
22514                 this.hideAction();
22515             }
22516         }
22517     },
22518
22519     storeXY : function(xy){
22520         delete this.lastLT;
22521         this.lastXY = xy;
22522     },
22523
22524     storeLeftTop : function(left, top){
22525         delete this.lastXY;
22526         this.lastLT = [left, top];
22527     },
22528
22529     // private
22530     beforeFx : function(){
22531         this.beforeAction();
22532         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22533     },
22534
22535     // private
22536     afterFx : function(){
22537         Roo.Layer.superclass.afterFx.apply(this, arguments);
22538         this.sync(this.isVisible());
22539     },
22540
22541     // private
22542     beforeAction : function(){
22543         if(!this.updating && this.shadow){
22544             this.shadow.hide();
22545         }
22546     },
22547
22548     // overridden Element method
22549     setLeft : function(left){
22550         this.storeLeftTop(left, this.getTop(true));
22551         supr.setLeft.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setTop : function(top){
22556         this.storeLeftTop(this.getLeft(true), top);
22557         supr.setTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setLeftTop : function(left, top){
22562         this.storeLeftTop(left, top);
22563         supr.setLeftTop.apply(this, arguments);
22564         this.sync();
22565     },
22566
22567     setXY : function(xy, a, d, c, e){
22568         this.fixDisplay();
22569         this.beforeAction();
22570         this.storeXY(xy);
22571         var cb = this.createCB(c);
22572         supr.setXY.call(this, xy, a, d, cb, e);
22573         if(!a){
22574             cb();
22575         }
22576     },
22577
22578     // private
22579     createCB : function(c){
22580         var el = this;
22581         return function(){
22582             el.constrainXY();
22583             el.sync(true);
22584             if(c){
22585                 c();
22586             }
22587         };
22588     },
22589
22590     // overridden Element method
22591     setX : function(x, a, d, c, e){
22592         this.setXY([x, this.getY()], a, d, c, e);
22593     },
22594
22595     // overridden Element method
22596     setY : function(y, a, d, c, e){
22597         this.setXY([this.getX(), y], a, d, c, e);
22598     },
22599
22600     // overridden Element method
22601     setSize : function(w, h, a, d, c, e){
22602         this.beforeAction();
22603         var cb = this.createCB(c);
22604         supr.setSize.call(this, w, h, a, d, cb, e);
22605         if(!a){
22606             cb();
22607         }
22608     },
22609
22610     // overridden Element method
22611     setWidth : function(w, a, d, c, e){
22612         this.beforeAction();
22613         var cb = this.createCB(c);
22614         supr.setWidth.call(this, w, a, d, cb, e);
22615         if(!a){
22616             cb();
22617         }
22618     },
22619
22620     // overridden Element method
22621     setHeight : function(h, a, d, c, e){
22622         this.beforeAction();
22623         var cb = this.createCB(c);
22624         supr.setHeight.call(this, h, a, d, cb, e);
22625         if(!a){
22626             cb();
22627         }
22628     },
22629
22630     // overridden Element method
22631     setBounds : function(x, y, w, h, a, d, c, e){
22632         this.beforeAction();
22633         var cb = this.createCB(c);
22634         if(!a){
22635             this.storeXY([x, y]);
22636             supr.setXY.call(this, [x, y]);
22637             supr.setSize.call(this, w, h, a, d, cb, e);
22638             cb();
22639         }else{
22640             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22641         }
22642         return this;
22643     },
22644     
22645     /**
22646      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22647      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22648      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22649      * @param {Number} zindex The new z-index to set
22650      * @return {this} The Layer
22651      */
22652     setZIndex : function(zindex){
22653         this.zindex = zindex;
22654         this.setStyle("z-index", zindex + 2);
22655         if(this.shadow){
22656             this.shadow.setZIndex(zindex + 1);
22657         }
22658         if(this.shim){
22659             this.shim.setStyle("z-index", zindex);
22660         }
22661     }
22662 });
22663 })();/*
22664  * Based on:
22665  * Ext JS Library 1.1.1
22666  * Copyright(c) 2006-2007, Ext JS, LLC.
22667  *
22668  * Originally Released Under LGPL - original licence link has changed is not relivant.
22669  *
22670  * Fork - LGPL
22671  * <script type="text/javascript">
22672  */
22673
22674
22675 /**
22676  * @class Roo.Shadow
22677  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22678  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22679  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22680  * @constructor
22681  * Create a new Shadow
22682  * @param {Object} config The config object
22683  */
22684 Roo.Shadow = function(config){
22685     Roo.apply(this, config);
22686     if(typeof this.mode != "string"){
22687         this.mode = this.defaultMode;
22688     }
22689     var o = this.offset, a = {h: 0};
22690     var rad = Math.floor(this.offset/2);
22691     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22692         case "drop":
22693             a.w = 0;
22694             a.l = a.t = o;
22695             a.t -= 1;
22696             if(Roo.isIE){
22697                 a.l -= this.offset + rad;
22698                 a.t -= this.offset + rad;
22699                 a.w -= rad;
22700                 a.h -= rad;
22701                 a.t += 1;
22702             }
22703         break;
22704         case "sides":
22705             a.w = (o*2);
22706             a.l = -o;
22707             a.t = o-1;
22708             if(Roo.isIE){
22709                 a.l -= (this.offset - rad);
22710                 a.t -= this.offset + rad;
22711                 a.l += 1;
22712                 a.w -= (this.offset - rad)*2;
22713                 a.w -= rad + 1;
22714                 a.h -= 1;
22715             }
22716         break;
22717         case "frame":
22718             a.w = a.h = (o*2);
22719             a.l = a.t = -o;
22720             a.t += 1;
22721             a.h -= 2;
22722             if(Roo.isIE){
22723                 a.l -= (this.offset - rad);
22724                 a.t -= (this.offset - rad);
22725                 a.l += 1;
22726                 a.w -= (this.offset + rad + 1);
22727                 a.h -= (this.offset + rad);
22728                 a.h += 1;
22729             }
22730         break;
22731     };
22732
22733     this.adjusts = a;
22734 };
22735
22736 Roo.Shadow.prototype = {
22737     /**
22738      * @cfg {String} mode
22739      * The shadow display mode.  Supports the following options:<br />
22740      * sides: Shadow displays on both sides and bottom only<br />
22741      * frame: Shadow displays equally on all four sides<br />
22742      * drop: Traditional bottom-right drop shadow (default)
22743      */
22744     /**
22745      * @cfg {String} offset
22746      * The number of pixels to offset the shadow from the element (defaults to 4)
22747      */
22748     offset: 4,
22749
22750     // private
22751     defaultMode: "drop",
22752
22753     /**
22754      * Displays the shadow under the target element
22755      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22756      */
22757     show : function(target){
22758         target = Roo.get(target);
22759         if(!this.el){
22760             this.el = Roo.Shadow.Pool.pull();
22761             if(this.el.dom.nextSibling != target.dom){
22762                 this.el.insertBefore(target);
22763             }
22764         }
22765         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22766         if(Roo.isIE){
22767             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22768         }
22769         this.realign(
22770             target.getLeft(true),
22771             target.getTop(true),
22772             target.getWidth(),
22773             target.getHeight()
22774         );
22775         this.el.dom.style.display = "block";
22776     },
22777
22778     /**
22779      * Returns true if the shadow is visible, else false
22780      */
22781     isVisible : function(){
22782         return this.el ? true : false;  
22783     },
22784
22785     /**
22786      * Direct alignment when values are already available. Show must be called at least once before
22787      * calling this method to ensure it is initialized.
22788      * @param {Number} left The target element left position
22789      * @param {Number} top The target element top position
22790      * @param {Number} width The target element width
22791      * @param {Number} height The target element height
22792      */
22793     realign : function(l, t, w, h){
22794         if(!this.el){
22795             return;
22796         }
22797         var a = this.adjusts, d = this.el.dom, s = d.style;
22798         var iea = 0;
22799         s.left = (l+a.l)+"px";
22800         s.top = (t+a.t)+"px";
22801         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22802  
22803         if(s.width != sws || s.height != shs){
22804             s.width = sws;
22805             s.height = shs;
22806             if(!Roo.isIE){
22807                 var cn = d.childNodes;
22808                 var sww = Math.max(0, (sw-12))+"px";
22809                 cn[0].childNodes[1].style.width = sww;
22810                 cn[1].childNodes[1].style.width = sww;
22811                 cn[2].childNodes[1].style.width = sww;
22812                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22813             }
22814         }
22815     },
22816
22817     /**
22818      * Hides this shadow
22819      */
22820     hide : function(){
22821         if(this.el){
22822             this.el.dom.style.display = "none";
22823             Roo.Shadow.Pool.push(this.el);
22824             delete this.el;
22825         }
22826     },
22827
22828     /**
22829      * Adjust the z-index of this shadow
22830      * @param {Number} zindex The new z-index
22831      */
22832     setZIndex : function(z){
22833         this.zIndex = z;
22834         if(this.el){
22835             this.el.setStyle("z-index", z);
22836         }
22837     }
22838 };
22839
22840 // Private utility class that manages the internal Shadow cache
22841 Roo.Shadow.Pool = function(){
22842     var p = [];
22843     var markup = Roo.isIE ?
22844                  '<div class="x-ie-shadow"></div>' :
22845                  '<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>';
22846     return {
22847         pull : function(){
22848             var sh = p.shift();
22849             if(!sh){
22850                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22851                 sh.autoBoxAdjust = false;
22852             }
22853             return sh;
22854         },
22855
22856         push : function(sh){
22857             p.push(sh);
22858         }
22859     };
22860 }();/*
22861  * Based on:
22862  * Ext JS Library 1.1.1
22863  * Copyright(c) 2006-2007, Ext JS, LLC.
22864  *
22865  * Originally Released Under LGPL - original licence link has changed is not relivant.
22866  *
22867  * Fork - LGPL
22868  * <script type="text/javascript">
22869  */
22870
22871 /**
22872  * @class Roo.BoxComponent
22873  * @extends Roo.Component
22874  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22875  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22876  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22877  * layout containers.
22878  * @constructor
22879  * @param {Roo.Element/String/Object} config The configuration options.
22880  */
22881 Roo.BoxComponent = function(config){
22882     Roo.Component.call(this, config);
22883     this.addEvents({
22884         /**
22885          * @event resize
22886          * Fires after the component is resized.
22887              * @param {Roo.Component} this
22888              * @param {Number} adjWidth The box-adjusted width that was set
22889              * @param {Number} adjHeight The box-adjusted height that was set
22890              * @param {Number} rawWidth The width that was originally specified
22891              * @param {Number} rawHeight The height that was originally specified
22892              */
22893         resize : true,
22894         /**
22895          * @event move
22896          * Fires after the component is moved.
22897              * @param {Roo.Component} this
22898              * @param {Number} x The new x position
22899              * @param {Number} y The new y position
22900              */
22901         move : true
22902     });
22903 };
22904
22905 Roo.extend(Roo.BoxComponent, Roo.Component, {
22906     // private, set in afterRender to signify that the component has been rendered
22907     boxReady : false,
22908     // private, used to defer height settings to subclasses
22909     deferHeight: false,
22910     /** @cfg {Number} width
22911      * width (optional) size of component
22912      */
22913      /** @cfg {Number} height
22914      * height (optional) size of component
22915      */
22916      
22917     /**
22918      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22919      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22920      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22921      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22922      * @return {Roo.BoxComponent} this
22923      */
22924     setSize : function(w, h){
22925         // support for standard size objects
22926         if(typeof w == 'object'){
22927             h = w.height;
22928             w = w.width;
22929         }
22930         // not rendered
22931         if(!this.boxReady){
22932             this.width = w;
22933             this.height = h;
22934             return this;
22935         }
22936
22937         // prevent recalcs when not needed
22938         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22939             return this;
22940         }
22941         this.lastSize = {width: w, height: h};
22942
22943         var adj = this.adjustSize(w, h);
22944         var aw = adj.width, ah = adj.height;
22945         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22946             var rz = this.getResizeEl();
22947             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22948                 rz.setSize(aw, ah);
22949             }else if(!this.deferHeight && ah !== undefined){
22950                 rz.setHeight(ah);
22951             }else if(aw !== undefined){
22952                 rz.setWidth(aw);
22953             }
22954             this.onResize(aw, ah, w, h);
22955             this.fireEvent('resize', this, aw, ah, w, h);
22956         }
22957         return this;
22958     },
22959
22960     /**
22961      * Gets the current size of the component's underlying element.
22962      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22963      */
22964     getSize : function(){
22965         return this.el.getSize();
22966     },
22967
22968     /**
22969      * Gets the current XY position of the component's underlying element.
22970      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22971      * @return {Array} The XY position of the element (e.g., [100, 200])
22972      */
22973     getPosition : function(local){
22974         if(local === true){
22975             return [this.el.getLeft(true), this.el.getTop(true)];
22976         }
22977         return this.xy || this.el.getXY();
22978     },
22979
22980     /**
22981      * Gets the current box measurements of the component's underlying element.
22982      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22983      * @returns {Object} box An object in the format {x, y, width, height}
22984      */
22985     getBox : function(local){
22986         var s = this.el.getSize();
22987         if(local){
22988             s.x = this.el.getLeft(true);
22989             s.y = this.el.getTop(true);
22990         }else{
22991             var xy = this.xy || this.el.getXY();
22992             s.x = xy[0];
22993             s.y = xy[1];
22994         }
22995         return s;
22996     },
22997
22998     /**
22999      * Sets the current box measurements of the component's underlying element.
23000      * @param {Object} box An object in the format {x, y, width, height}
23001      * @returns {Roo.BoxComponent} this
23002      */
23003     updateBox : function(box){
23004         this.setSize(box.width, box.height);
23005         this.setPagePosition(box.x, box.y);
23006         return this;
23007     },
23008
23009     // protected
23010     getResizeEl : function(){
23011         return this.resizeEl || this.el;
23012     },
23013
23014     // protected
23015     getPositionEl : function(){
23016         return this.positionEl || this.el;
23017     },
23018
23019     /**
23020      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23021      * This method fires the move event.
23022      * @param {Number} left The new left
23023      * @param {Number} top The new top
23024      * @returns {Roo.BoxComponent} this
23025      */
23026     setPosition : function(x, y){
23027         this.x = x;
23028         this.y = y;
23029         if(!this.boxReady){
23030             return this;
23031         }
23032         var adj = this.adjustPosition(x, y);
23033         var ax = adj.x, ay = adj.y;
23034
23035         var el = this.getPositionEl();
23036         if(ax !== undefined || ay !== undefined){
23037             if(ax !== undefined && ay !== undefined){
23038                 el.setLeftTop(ax, ay);
23039             }else if(ax !== undefined){
23040                 el.setLeft(ax);
23041             }else if(ay !== undefined){
23042                 el.setTop(ay);
23043             }
23044             this.onPosition(ax, ay);
23045             this.fireEvent('move', this, ax, ay);
23046         }
23047         return this;
23048     },
23049
23050     /**
23051      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23052      * This method fires the move event.
23053      * @param {Number} x The new x position
23054      * @param {Number} y The new y position
23055      * @returns {Roo.BoxComponent} this
23056      */
23057     setPagePosition : function(x, y){
23058         this.pageX = x;
23059         this.pageY = y;
23060         if(!this.boxReady){
23061             return;
23062         }
23063         if(x === undefined || y === undefined){ // cannot translate undefined points
23064             return;
23065         }
23066         var p = this.el.translatePoints(x, y);
23067         this.setPosition(p.left, p.top);
23068         return this;
23069     },
23070
23071     // private
23072     onRender : function(ct, position){
23073         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23074         if(this.resizeEl){
23075             this.resizeEl = Roo.get(this.resizeEl);
23076         }
23077         if(this.positionEl){
23078             this.positionEl = Roo.get(this.positionEl);
23079         }
23080     },
23081
23082     // private
23083     afterRender : function(){
23084         Roo.BoxComponent.superclass.afterRender.call(this);
23085         this.boxReady = true;
23086         this.setSize(this.width, this.height);
23087         if(this.x || this.y){
23088             this.setPosition(this.x, this.y);
23089         }
23090         if(this.pageX || this.pageY){
23091             this.setPagePosition(this.pageX, this.pageY);
23092         }
23093     },
23094
23095     /**
23096      * Force the component's size to recalculate based on the underlying element's current height and width.
23097      * @returns {Roo.BoxComponent} this
23098      */
23099     syncSize : function(){
23100         delete this.lastSize;
23101         this.setSize(this.el.getWidth(), this.el.getHeight());
23102         return this;
23103     },
23104
23105     /**
23106      * Called after the component is resized, this method is empty by default but can be implemented by any
23107      * subclass that needs to perform custom logic after a resize occurs.
23108      * @param {Number} adjWidth The box-adjusted width that was set
23109      * @param {Number} adjHeight The box-adjusted height that was set
23110      * @param {Number} rawWidth The width that was originally specified
23111      * @param {Number} rawHeight The height that was originally specified
23112      */
23113     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23114
23115     },
23116
23117     /**
23118      * Called after the component is moved, this method is empty by default but can be implemented by any
23119      * subclass that needs to perform custom logic after a move occurs.
23120      * @param {Number} x The new x position
23121      * @param {Number} y The new y position
23122      */
23123     onPosition : function(x, y){
23124
23125     },
23126
23127     // private
23128     adjustSize : function(w, h){
23129         if(this.autoWidth){
23130             w = 'auto';
23131         }
23132         if(this.autoHeight){
23133             h = 'auto';
23134         }
23135         return {width : w, height: h};
23136     },
23137
23138     // private
23139     adjustPosition : function(x, y){
23140         return {x : x, y: y};
23141     }
23142 });/*
23143  * Based on:
23144  * Ext JS Library 1.1.1
23145  * Copyright(c) 2006-2007, Ext JS, LLC.
23146  *
23147  * Originally Released Under LGPL - original licence link has changed is not relivant.
23148  *
23149  * Fork - LGPL
23150  * <script type="text/javascript">
23151  */
23152
23153
23154 /**
23155  * @class Roo.SplitBar
23156  * @extends Roo.util.Observable
23157  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23158  * <br><br>
23159  * Usage:
23160  * <pre><code>
23161 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23162                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23163 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23164 split.minSize = 100;
23165 split.maxSize = 600;
23166 split.animate = true;
23167 split.on('moved', splitterMoved);
23168 </code></pre>
23169  * @constructor
23170  * Create a new SplitBar
23171  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23172  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23173  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23174  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23175                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23176                         position of the SplitBar).
23177  */
23178 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23179     
23180     /** @private */
23181     this.el = Roo.get(dragElement, true);
23182     this.el.dom.unselectable = "on";
23183     /** @private */
23184     this.resizingEl = Roo.get(resizingElement, true);
23185
23186     /**
23187      * @private
23188      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23189      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23190      * @type Number
23191      */
23192     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23193     
23194     /**
23195      * The minimum size of the resizing element. (Defaults to 0)
23196      * @type Number
23197      */
23198     this.minSize = 0;
23199     
23200     /**
23201      * The maximum size of the resizing element. (Defaults to 2000)
23202      * @type Number
23203      */
23204     this.maxSize = 2000;
23205     
23206     /**
23207      * Whether to animate the transition to the new size
23208      * @type Boolean
23209      */
23210     this.animate = false;
23211     
23212     /**
23213      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23214      * @type Boolean
23215      */
23216     this.useShim = false;
23217     
23218     /** @private */
23219     this.shim = null;
23220     
23221     if(!existingProxy){
23222         /** @private */
23223         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23224     }else{
23225         this.proxy = Roo.get(existingProxy).dom;
23226     }
23227     /** @private */
23228     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23229     
23230     /** @private */
23231     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23232     
23233     /** @private */
23234     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23235     
23236     /** @private */
23237     this.dragSpecs = {};
23238     
23239     /**
23240      * @private The adapter to use to positon and resize elements
23241      */
23242     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23243     this.adapter.init(this);
23244     
23245     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23246         /** @private */
23247         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23248         this.el.addClass("x-splitbar-h");
23249     }else{
23250         /** @private */
23251         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23252         this.el.addClass("x-splitbar-v");
23253     }
23254     
23255     this.addEvents({
23256         /**
23257          * @event resize
23258          * Fires when the splitter is moved (alias for {@link #event-moved})
23259          * @param {Roo.SplitBar} this
23260          * @param {Number} newSize the new width or height
23261          */
23262         "resize" : true,
23263         /**
23264          * @event moved
23265          * Fires when the splitter is moved
23266          * @param {Roo.SplitBar} this
23267          * @param {Number} newSize the new width or height
23268          */
23269         "moved" : true,
23270         /**
23271          * @event beforeresize
23272          * Fires before the splitter is dragged
23273          * @param {Roo.SplitBar} this
23274          */
23275         "beforeresize" : true,
23276
23277         "beforeapply" : true
23278     });
23279
23280     Roo.util.Observable.call(this);
23281 };
23282
23283 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23284     onStartProxyDrag : function(x, y){
23285         this.fireEvent("beforeresize", this);
23286         if(!this.overlay){
23287             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23288             o.unselectable();
23289             o.enableDisplayMode("block");
23290             // all splitbars share the same overlay
23291             Roo.SplitBar.prototype.overlay = o;
23292         }
23293         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23294         this.overlay.show();
23295         Roo.get(this.proxy).setDisplayed("block");
23296         var size = this.adapter.getElementSize(this);
23297         this.activeMinSize = this.getMinimumSize();;
23298         this.activeMaxSize = this.getMaximumSize();;
23299         var c1 = size - this.activeMinSize;
23300         var c2 = Math.max(this.activeMaxSize - size, 0);
23301         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23302             this.dd.resetConstraints();
23303             this.dd.setXConstraint(
23304                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23305                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23306             );
23307             this.dd.setYConstraint(0, 0);
23308         }else{
23309             this.dd.resetConstraints();
23310             this.dd.setXConstraint(0, 0);
23311             this.dd.setYConstraint(
23312                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23313                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23314             );
23315          }
23316         this.dragSpecs.startSize = size;
23317         this.dragSpecs.startPoint = [x, y];
23318         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23319     },
23320     
23321     /** 
23322      * @private Called after the drag operation by the DDProxy
23323      */
23324     onEndProxyDrag : function(e){
23325         Roo.get(this.proxy).setDisplayed(false);
23326         var endPoint = Roo.lib.Event.getXY(e);
23327         if(this.overlay){
23328             this.overlay.hide();
23329         }
23330         var newSize;
23331         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.LEFT ?
23334                     endPoint[0] - this.dragSpecs.startPoint[0] :
23335                     this.dragSpecs.startPoint[0] - endPoint[0]
23336                 );
23337         }else{
23338             newSize = this.dragSpecs.startSize + 
23339                 (this.placement == Roo.SplitBar.TOP ?
23340                     endPoint[1] - this.dragSpecs.startPoint[1] :
23341                     this.dragSpecs.startPoint[1] - endPoint[1]
23342                 );
23343         }
23344         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23345         if(newSize != this.dragSpecs.startSize){
23346             if(this.fireEvent('beforeapply', this, newSize) !== false){
23347                 this.adapter.setElementSize(this, newSize);
23348                 this.fireEvent("moved", this, newSize);
23349                 this.fireEvent("resize", this, newSize);
23350             }
23351         }
23352     },
23353     
23354     /**
23355      * Get the adapter this SplitBar uses
23356      * @return The adapter object
23357      */
23358     getAdapter : function(){
23359         return this.adapter;
23360     },
23361     
23362     /**
23363      * Set the adapter this SplitBar uses
23364      * @param {Object} adapter A SplitBar adapter object
23365      */
23366     setAdapter : function(adapter){
23367         this.adapter = adapter;
23368         this.adapter.init(this);
23369     },
23370     
23371     /**
23372      * Gets the minimum size for the resizing element
23373      * @return {Number} The minimum size
23374      */
23375     getMinimumSize : function(){
23376         return this.minSize;
23377     },
23378     
23379     /**
23380      * Sets the minimum size for the resizing element
23381      * @param {Number} minSize The minimum size
23382      */
23383     setMinimumSize : function(minSize){
23384         this.minSize = minSize;
23385     },
23386     
23387     /**
23388      * Gets the maximum size for the resizing element
23389      * @return {Number} The maximum size
23390      */
23391     getMaximumSize : function(){
23392         return this.maxSize;
23393     },
23394     
23395     /**
23396      * Sets the maximum size for the resizing element
23397      * @param {Number} maxSize The maximum size
23398      */
23399     setMaximumSize : function(maxSize){
23400         this.maxSize = maxSize;
23401     },
23402     
23403     /**
23404      * Sets the initialize size for the resizing element
23405      * @param {Number} size The initial size
23406      */
23407     setCurrentSize : function(size){
23408         var oldAnimate = this.animate;
23409         this.animate = false;
23410         this.adapter.setElementSize(this, size);
23411         this.animate = oldAnimate;
23412     },
23413     
23414     /**
23415      * Destroy this splitbar. 
23416      * @param {Boolean} removeEl True to remove the element
23417      */
23418     destroy : function(removeEl){
23419         if(this.shim){
23420             this.shim.remove();
23421         }
23422         this.dd.unreg();
23423         this.proxy.parentNode.removeChild(this.proxy);
23424         if(removeEl){
23425             this.el.remove();
23426         }
23427     }
23428 });
23429
23430 /**
23431  * @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.
23432  */
23433 Roo.SplitBar.createProxy = function(dir){
23434     var proxy = new Roo.Element(document.createElement("div"));
23435     proxy.unselectable();
23436     var cls = 'x-splitbar-proxy';
23437     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23438     document.body.appendChild(proxy.dom);
23439     return proxy.dom;
23440 };
23441
23442 /** 
23443  * @class Roo.SplitBar.BasicLayoutAdapter
23444  * Default Adapter. It assumes the splitter and resizing element are not positioned
23445  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23446  */
23447 Roo.SplitBar.BasicLayoutAdapter = function(){
23448 };
23449
23450 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23451     // do nothing for now
23452     init : function(s){
23453     
23454     },
23455     /**
23456      * Called before drag operations to get the current size of the resizing element. 
23457      * @param {Roo.SplitBar} s The SplitBar using this adapter
23458      */
23459      getElementSize : function(s){
23460         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23461             return s.resizingEl.getWidth();
23462         }else{
23463             return s.resizingEl.getHeight();
23464         }
23465     },
23466     
23467     /**
23468      * Called after drag operations to set the size of the resizing element.
23469      * @param {Roo.SplitBar} s The SplitBar using this adapter
23470      * @param {Number} newSize The new size to set
23471      * @param {Function} onComplete A function to be invoked when resizing is complete
23472      */
23473     setElementSize : function(s, newSize, onComplete){
23474         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23475             if(!s.animate){
23476                 s.resizingEl.setWidth(newSize);
23477                 if(onComplete){
23478                     onComplete(s, newSize);
23479                 }
23480             }else{
23481                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23482             }
23483         }else{
23484             
23485             if(!s.animate){
23486                 s.resizingEl.setHeight(newSize);
23487                 if(onComplete){
23488                     onComplete(s, newSize);
23489                 }
23490             }else{
23491                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23492             }
23493         }
23494     }
23495 };
23496
23497 /** 
23498  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23499  * @extends Roo.SplitBar.BasicLayoutAdapter
23500  * Adapter that  moves the splitter element to align with the resized sizing element. 
23501  * Used with an absolute positioned SplitBar.
23502  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23503  * document.body, make sure you assign an id to the body element.
23504  */
23505 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23506     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23507     this.container = Roo.get(container);
23508 };
23509
23510 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23511     init : function(s){
23512         this.basic.init(s);
23513     },
23514     
23515     getElementSize : function(s){
23516         return this.basic.getElementSize(s);
23517     },
23518     
23519     setElementSize : function(s, newSize, onComplete){
23520         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23521     },
23522     
23523     moveSplitter : function(s){
23524         var yes = Roo.SplitBar;
23525         switch(s.placement){
23526             case yes.LEFT:
23527                 s.el.setX(s.resizingEl.getRight());
23528                 break;
23529             case yes.RIGHT:
23530                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23531                 break;
23532             case yes.TOP:
23533                 s.el.setY(s.resizingEl.getBottom());
23534                 break;
23535             case yes.BOTTOM:
23536                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23537                 break;
23538         }
23539     }
23540 };
23541
23542 /**
23543  * Orientation constant - Create a vertical SplitBar
23544  * @static
23545  * @type Number
23546  */
23547 Roo.SplitBar.VERTICAL = 1;
23548
23549 /**
23550  * Orientation constant - Create a horizontal SplitBar
23551  * @static
23552  * @type Number
23553  */
23554 Roo.SplitBar.HORIZONTAL = 2;
23555
23556 /**
23557  * Placement constant - The resizing element is to the left of the splitter element
23558  * @static
23559  * @type Number
23560  */
23561 Roo.SplitBar.LEFT = 1;
23562
23563 /**
23564  * Placement constant - The resizing element is to the right of the splitter element
23565  * @static
23566  * @type Number
23567  */
23568 Roo.SplitBar.RIGHT = 2;
23569
23570 /**
23571  * Placement constant - The resizing element is positioned above the splitter element
23572  * @static
23573  * @type Number
23574  */
23575 Roo.SplitBar.TOP = 3;
23576
23577 /**
23578  * Placement constant - The resizing element is positioned under splitter element
23579  * @static
23580  * @type Number
23581  */
23582 Roo.SplitBar.BOTTOM = 4;
23583 /*
23584  * Based on:
23585  * Ext JS Library 1.1.1
23586  * Copyright(c) 2006-2007, Ext JS, LLC.
23587  *
23588  * Originally Released Under LGPL - original licence link has changed is not relivant.
23589  *
23590  * Fork - LGPL
23591  * <script type="text/javascript">
23592  */
23593
23594 /**
23595  * @class Roo.View
23596  * @extends Roo.util.Observable
23597  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23598  * This class also supports single and multi selection modes. <br>
23599  * Create a data model bound view:
23600  <pre><code>
23601  var store = new Roo.data.Store(...);
23602
23603  var view = new Roo.View({
23604     el : "my-element",
23605     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23606  
23607     singleSelect: true,
23608     selectedClass: "ydataview-selected",
23609     store: store
23610  });
23611
23612  // listen for node click?
23613  view.on("click", function(vw, index, node, e){
23614  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23615  });
23616
23617  // load XML data
23618  dataModel.load("foobar.xml");
23619  </code></pre>
23620  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23621  * <br><br>
23622  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23623  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23624  * 
23625  * Note: old style constructor is still suported (container, template, config)
23626  * 
23627  * @constructor
23628  * Create a new View
23629  * @param {Object} config The config object
23630  * 
23631  */
23632 Roo.View = function(config, depreciated_tpl, depreciated_config){
23633     
23634     if (typeof(depreciated_tpl) == 'undefined') {
23635         // new way.. - universal constructor.
23636         Roo.apply(this, config);
23637         this.el  = Roo.get(this.el);
23638     } else {
23639         // old format..
23640         this.el  = Roo.get(config);
23641         this.tpl = depreciated_tpl;
23642         Roo.apply(this, depreciated_config);
23643     }
23644      
23645     
23646     if(typeof(this.tpl) == "string"){
23647         this.tpl = new Roo.Template(this.tpl);
23648     } else {
23649         // support xtype ctors..
23650         this.tpl = new Roo.factory(this.tpl, Roo);
23651     }
23652     
23653     
23654     this.tpl.compile();
23655    
23656
23657      
23658     /** @private */
23659     this.addEvents({
23660         /**
23661          * @event beforeclick
23662          * Fires before a click is processed. Returns false to cancel the default action.
23663          * @param {Roo.View} this
23664          * @param {Number} index The index of the target node
23665          * @param {HTMLElement} node The target node
23666          * @param {Roo.EventObject} e The raw event object
23667          */
23668             "beforeclick" : true,
23669         /**
23670          * @event click
23671          * Fires when a template node is clicked.
23672          * @param {Roo.View} this
23673          * @param {Number} index The index of the target node
23674          * @param {HTMLElement} node The target node
23675          * @param {Roo.EventObject} e The raw event object
23676          */
23677             "click" : true,
23678         /**
23679          * @event dblclick
23680          * Fires when a template node is double clicked.
23681          * @param {Roo.View} this
23682          * @param {Number} index The index of the target node
23683          * @param {HTMLElement} node The target node
23684          * @param {Roo.EventObject} e The raw event object
23685          */
23686             "dblclick" : true,
23687         /**
23688          * @event contextmenu
23689          * Fires when a template node is right clicked.
23690          * @param {Roo.View} this
23691          * @param {Number} index The index of the target node
23692          * @param {HTMLElement} node The target node
23693          * @param {Roo.EventObject} e The raw event object
23694          */
23695             "contextmenu" : true,
23696         /**
23697          * @event selectionchange
23698          * Fires when the selected nodes change.
23699          * @param {Roo.View} this
23700          * @param {Array} selections Array of the selected nodes
23701          */
23702             "selectionchange" : true,
23703     
23704         /**
23705          * @event beforeselect
23706          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23707          * @param {Roo.View} this
23708          * @param {HTMLElement} node The node to be selected
23709          * @param {Array} selections Array of currently selected nodes
23710          */
23711             "beforeselect" : true,
23712         /**
23713          * @event preparedata
23714          * Fires on every row to render, to allow you to change the data.
23715          * @param {Roo.View} this
23716          * @param {Object} data to be rendered (change this)
23717          */
23718           "preparedata" : true
23719         });
23720
23721     this.el.on({
23722         "click": this.onClick,
23723         "dblclick": this.onDblClick,
23724         "contextmenu": this.onContextMenu,
23725         scope:this
23726     });
23727
23728     this.selections = [];
23729     this.nodes = [];
23730     this.cmp = new Roo.CompositeElementLite([]);
23731     if(this.store){
23732         this.store = Roo.factory(this.store, Roo.data);
23733         this.setStore(this.store, true);
23734     }
23735     Roo.View.superclass.constructor.call(this);
23736 };
23737
23738 Roo.extend(Roo.View, Roo.util.Observable, {
23739     
23740      /**
23741      * @cfg {Roo.data.Store} store Data store to load data from.
23742      */
23743     store : false,
23744     
23745     /**
23746      * @cfg {String|Roo.Element} el The container element.
23747      */
23748     el : '',
23749     
23750     /**
23751      * @cfg {String|Roo.Template} tpl The template used by this View 
23752      */
23753     tpl : false,
23754     
23755     /**
23756      * @cfg {String} selectedClass The css class to add to selected nodes
23757      */
23758     selectedClass : "x-view-selected",
23759      /**
23760      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23761      */
23762     emptyText : "",
23763     /**
23764      * @cfg {Boolean} multiSelect Allow multiple selection
23765      */
23766     multiSelect : false,
23767     /**
23768      * @cfg {Boolean} singleSelect Allow single selection
23769      */
23770     singleSelect:  false,
23771     
23772     /**
23773      * @cfg {Boolean} toggleSelect - selecting 
23774      */
23775     toggleSelect : false,
23776     
23777     /**
23778      * Returns the element this view is bound to.
23779      * @return {Roo.Element}
23780      */
23781     getEl : function(){
23782         return this.el;
23783     },
23784
23785     /**
23786      * Refreshes the view.
23787      */
23788     refresh : function(){
23789         var t = this.tpl;
23790         this.clearSelections();
23791         this.el.update("");
23792         var html = [];
23793         var records = this.store.getRange();
23794         if(records.length < 1){
23795             this.el.update(this.emptyText);
23796             return;
23797         }
23798         for(var i = 0, len = records.length; i < len; i++){
23799             var data = this.prepareData(records[i].data, i, records[i]);
23800             this.fireEvent("preparedata", this, data, i, records[i]);
23801             html[html.length] = t.apply(data);
23802         }
23803         this.el.update(html.join(""));
23804         this.nodes = this.el.dom.childNodes;
23805         this.updateIndexes(0);
23806     },
23807
23808     /**
23809      * Function to override to reformat the data that is sent to
23810      * the template for each node.
23811      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23812      * a JSON object for an UpdateManager bound view).
23813      */
23814     prepareData : function(data){
23815         return data;
23816     },
23817
23818     onUpdate : function(ds, record){
23819         this.clearSelections();
23820         var index = this.store.indexOf(record);
23821         var n = this.nodes[index];
23822         this.tpl.insertBefore(n, this.prepareData(record.data));
23823         n.parentNode.removeChild(n);
23824         this.updateIndexes(index, index);
23825     },
23826
23827     onAdd : function(ds, records, index){
23828         this.clearSelections();
23829         if(this.nodes.length == 0){
23830             this.refresh();
23831             return;
23832         }
23833         var n = this.nodes[index];
23834         for(var i = 0, len = records.length; i < len; i++){
23835             var d = this.prepareData(records[i].data);
23836             if(n){
23837                 this.tpl.insertBefore(n, d);
23838             }else{
23839                 this.tpl.append(this.el, d);
23840             }
23841         }
23842         this.updateIndexes(index);
23843     },
23844
23845     onRemove : function(ds, record, index){
23846         this.clearSelections();
23847         this.el.dom.removeChild(this.nodes[index]);
23848         this.updateIndexes(index);
23849     },
23850
23851     /**
23852      * Refresh an individual node.
23853      * @param {Number} index
23854      */
23855     refreshNode : function(index){
23856         this.onUpdate(this.store, this.store.getAt(index));
23857     },
23858
23859     updateIndexes : function(startIndex, endIndex){
23860         var ns = this.nodes;
23861         startIndex = startIndex || 0;
23862         endIndex = endIndex || ns.length - 1;
23863         for(var i = startIndex; i <= endIndex; i++){
23864             ns[i].nodeIndex = i;
23865         }
23866     },
23867
23868     /**
23869      * Changes the data store this view uses and refresh the view.
23870      * @param {Store} store
23871      */
23872     setStore : function(store, initial){
23873         if(!initial && this.store){
23874             this.store.un("datachanged", this.refresh);
23875             this.store.un("add", this.onAdd);
23876             this.store.un("remove", this.onRemove);
23877             this.store.un("update", this.onUpdate);
23878             this.store.un("clear", this.refresh);
23879         }
23880         if(store){
23881           
23882             store.on("datachanged", this.refresh, this);
23883             store.on("add", this.onAdd, this);
23884             store.on("remove", this.onRemove, this);
23885             store.on("update", this.onUpdate, this);
23886             store.on("clear", this.refresh, this);
23887         }
23888         
23889         if(store){
23890             this.refresh();
23891         }
23892     },
23893
23894     /**
23895      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23896      * @param {HTMLElement} node
23897      * @return {HTMLElement} The template node
23898      */
23899     findItemFromChild : function(node){
23900         var el = this.el.dom;
23901         if(!node || node.parentNode == el){
23902                     return node;
23903             }
23904             var p = node.parentNode;
23905             while(p && p != el){
23906             if(p.parentNode == el){
23907                 return p;
23908             }
23909             p = p.parentNode;
23910         }
23911             return null;
23912     },
23913
23914     /** @ignore */
23915     onClick : function(e){
23916         var item = this.findItemFromChild(e.getTarget());
23917         if(item){
23918             var index = this.indexOf(item);
23919             if(this.onItemClick(item, index, e) !== false){
23920                 this.fireEvent("click", this, index, item, e);
23921             }
23922         }else{
23923             this.clearSelections();
23924         }
23925     },
23926
23927     /** @ignore */
23928     onContextMenu : function(e){
23929         var item = this.findItemFromChild(e.getTarget());
23930         if(item){
23931             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23932         }
23933     },
23934
23935     /** @ignore */
23936     onDblClick : function(e){
23937         var item = this.findItemFromChild(e.getTarget());
23938         if(item){
23939             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23940         }
23941     },
23942
23943     onItemClick : function(item, index, e)
23944     {
23945         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23946             return false;
23947         }
23948         if (this.toggleSelect) {
23949             var m = this.isSelected(item) ? 'unselect' : 'select';
23950             Roo.log(m);
23951             var _t = this;
23952             _t[m](item, true, false);
23953             return true;
23954         }
23955         if(this.multiSelect || this.singleSelect){
23956             if(this.multiSelect && e.shiftKey && this.lastSelection){
23957                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23958             }else{
23959                 this.select(item, this.multiSelect && e.ctrlKey);
23960                 this.lastSelection = item;
23961             }
23962             e.preventDefault();
23963         }
23964         return true;
23965     },
23966
23967     /**
23968      * Get the number of selected nodes.
23969      * @return {Number}
23970      */
23971     getSelectionCount : function(){
23972         return this.selections.length;
23973     },
23974
23975     /**
23976      * Get the currently selected nodes.
23977      * @return {Array} An array of HTMLElements
23978      */
23979     getSelectedNodes : function(){
23980         return this.selections;
23981     },
23982
23983     /**
23984      * Get the indexes of the selected nodes.
23985      * @return {Array}
23986      */
23987     getSelectedIndexes : function(){
23988         var indexes = [], s = this.selections;
23989         for(var i = 0, len = s.length; i < len; i++){
23990             indexes.push(s[i].nodeIndex);
23991         }
23992         return indexes;
23993     },
23994
23995     /**
23996      * Clear all selections
23997      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23998      */
23999     clearSelections : function(suppressEvent){
24000         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24001             this.cmp.elements = this.selections;
24002             this.cmp.removeClass(this.selectedClass);
24003             this.selections = [];
24004             if(!suppressEvent){
24005                 this.fireEvent("selectionchange", this, this.selections);
24006             }
24007         }
24008     },
24009
24010     /**
24011      * Returns true if the passed node is selected
24012      * @param {HTMLElement/Number} node The node or node index
24013      * @return {Boolean}
24014      */
24015     isSelected : function(node){
24016         var s = this.selections;
24017         if(s.length < 1){
24018             return false;
24019         }
24020         node = this.getNode(node);
24021         return s.indexOf(node) !== -1;
24022     },
24023
24024     /**
24025      * Selects nodes.
24026      * @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
24027      * @param {Boolean} keepExisting (optional) true to keep existing selections
24028      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24029      */
24030     select : function(nodeInfo, keepExisting, suppressEvent){
24031         if(nodeInfo instanceof Array){
24032             if(!keepExisting){
24033                 this.clearSelections(true);
24034             }
24035             for(var i = 0, len = nodeInfo.length; i < len; i++){
24036                 this.select(nodeInfo[i], true, true);
24037             }
24038             return;
24039         } 
24040         var node = this.getNode(nodeInfo);
24041         if(!node || this.isSelected(node)){
24042             return; // already selected.
24043         }
24044         if(!keepExisting){
24045             this.clearSelections(true);
24046         }
24047         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24048             Roo.fly(node).addClass(this.selectedClass);
24049             this.selections.push(node);
24050             if(!suppressEvent){
24051                 this.fireEvent("selectionchange", this, this.selections);
24052             }
24053         }
24054         
24055         
24056     },
24057       /**
24058      * Unselects nodes.
24059      * @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
24060      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24061      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24062      */
24063     unselect : function(nodeInfo, keepExisting, suppressEvent)
24064     {
24065         if(nodeInfo instanceof Array){
24066             Roo.each(this.selections, function(s) {
24067                 this.unselect(s, nodeInfo);
24068             }, this);
24069             return;
24070         }
24071         var node = this.getNode(nodeInfo);
24072         if(!node || !this.isSelected(node)){
24073             Roo.log("not selected");
24074             return; // not selected.
24075         }
24076         // fireevent???
24077         var ns = [];
24078         Roo.each(this.selections, function(s) {
24079             if (s == node ) {
24080                 Roo.fly(node).removeClass(this.selectedClass);
24081
24082                 return;
24083             }
24084             ns.push(s);
24085         },this);
24086         
24087         this.selections= ns;
24088         this.fireEvent("selectionchange", this, this.selections);
24089     },
24090
24091     /**
24092      * Gets a template node.
24093      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24094      * @return {HTMLElement} The node or null if it wasn't found
24095      */
24096     getNode : function(nodeInfo){
24097         if(typeof nodeInfo == "string"){
24098             return document.getElementById(nodeInfo);
24099         }else if(typeof nodeInfo == "number"){
24100             return this.nodes[nodeInfo];
24101         }
24102         return nodeInfo;
24103     },
24104
24105     /**
24106      * Gets a range template nodes.
24107      * @param {Number} startIndex
24108      * @param {Number} endIndex
24109      * @return {Array} An array of nodes
24110      */
24111     getNodes : function(start, end){
24112         var ns = this.nodes;
24113         start = start || 0;
24114         end = typeof end == "undefined" ? ns.length - 1 : end;
24115         var nodes = [];
24116         if(start <= end){
24117             for(var i = start; i <= end; i++){
24118                 nodes.push(ns[i]);
24119             }
24120         } else{
24121             for(var i = start; i >= end; i--){
24122                 nodes.push(ns[i]);
24123             }
24124         }
24125         return nodes;
24126     },
24127
24128     /**
24129      * Finds the index of the passed node
24130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24131      * @return {Number} The index of the node or -1
24132      */
24133     indexOf : function(node){
24134         node = this.getNode(node);
24135         if(typeof node.nodeIndex == "number"){
24136             return node.nodeIndex;
24137         }
24138         var ns = this.nodes;
24139         for(var i = 0, len = ns.length; i < len; i++){
24140             if(ns[i] == node){
24141                 return i;
24142             }
24143         }
24144         return -1;
24145     }
24146 });
24147 /*
24148  * Based on:
24149  * Ext JS Library 1.1.1
24150  * Copyright(c) 2006-2007, Ext JS, LLC.
24151  *
24152  * Originally Released Under LGPL - original licence link has changed is not relivant.
24153  *
24154  * Fork - LGPL
24155  * <script type="text/javascript">
24156  */
24157
24158 /**
24159  * @class Roo.JsonView
24160  * @extends Roo.View
24161  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24162 <pre><code>
24163 var view = new Roo.JsonView({
24164     container: "my-element",
24165     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24166     multiSelect: true, 
24167     jsonRoot: "data" 
24168 });
24169
24170 // listen for node click?
24171 view.on("click", function(vw, index, node, e){
24172     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24173 });
24174
24175 // direct load of JSON data
24176 view.load("foobar.php");
24177
24178 // Example from my blog list
24179 var tpl = new Roo.Template(
24180     '&lt;div class="entry"&gt;' +
24181     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24182     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24183     "&lt;/div&gt;&lt;hr /&gt;"
24184 );
24185
24186 var moreView = new Roo.JsonView({
24187     container :  "entry-list", 
24188     template : tpl,
24189     jsonRoot: "posts"
24190 });
24191 moreView.on("beforerender", this.sortEntries, this);
24192 moreView.load({
24193     url: "/blog/get-posts.php",
24194     params: "allposts=true",
24195     text: "Loading Blog Entries..."
24196 });
24197 </code></pre>
24198
24199 * Note: old code is supported with arguments : (container, template, config)
24200
24201
24202  * @constructor
24203  * Create a new JsonView
24204  * 
24205  * @param {Object} config The config object
24206  * 
24207  */
24208 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24209     
24210     
24211     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24212
24213     var um = this.el.getUpdateManager();
24214     um.setRenderer(this);
24215     um.on("update", this.onLoad, this);
24216     um.on("failure", this.onLoadException, this);
24217
24218     /**
24219      * @event beforerender
24220      * Fires before rendering of the downloaded JSON data.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      */
24224     /**
24225      * @event load
24226      * Fires when data is loaded.
24227      * @param {Roo.JsonView} this
24228      * @param {Object} data The JSON data loaded
24229      * @param {Object} response The raw Connect response object
24230      */
24231     /**
24232      * @event loadexception
24233      * Fires when loading fails.
24234      * @param {Roo.JsonView} this
24235      * @param {Object} response The raw Connect response object
24236      */
24237     this.addEvents({
24238         'beforerender' : true,
24239         'load' : true,
24240         'loadexception' : true
24241     });
24242 };
24243 Roo.extend(Roo.JsonView, Roo.View, {
24244     /**
24245      * @type {String} The root property in the loaded JSON object that contains the data
24246      */
24247     jsonRoot : "",
24248
24249     /**
24250      * Refreshes the view.
24251      */
24252     refresh : function(){
24253         this.clearSelections();
24254         this.el.update("");
24255         var html = [];
24256         var o = this.jsonData;
24257         if(o && o.length > 0){
24258             for(var i = 0, len = o.length; i < len; i++){
24259                 var data = this.prepareData(o[i], i, o);
24260                 html[html.length] = this.tpl.apply(data);
24261             }
24262         }else{
24263             html.push(this.emptyText);
24264         }
24265         this.el.update(html.join(""));
24266         this.nodes = this.el.dom.childNodes;
24267         this.updateIndexes(0);
24268     },
24269
24270     /**
24271      * 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.
24272      * @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:
24273      <pre><code>
24274      view.load({
24275          url: "your-url.php",
24276          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24277          callback: yourFunction,
24278          scope: yourObject, //(optional scope)
24279          discardUrl: false,
24280          nocache: false,
24281          text: "Loading...",
24282          timeout: 30,
24283          scripts: false
24284      });
24285      </code></pre>
24286      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24287      * 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.
24288      * @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}
24289      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24290      * @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.
24291      */
24292     load : function(){
24293         var um = this.el.getUpdateManager();
24294         um.update.apply(um, arguments);
24295     },
24296
24297     render : function(el, response){
24298         this.clearSelections();
24299         this.el.update("");
24300         var o;
24301         try{
24302             o = Roo.util.JSON.decode(response.responseText);
24303             if(this.jsonRoot){
24304                 
24305                 o = o[this.jsonRoot];
24306             }
24307         } catch(e){
24308         }
24309         /**
24310          * The current JSON data or null
24311          */
24312         this.jsonData = o;
24313         this.beforeRender();
24314         this.refresh();
24315     },
24316
24317 /**
24318  * Get the number of records in the current JSON dataset
24319  * @return {Number}
24320  */
24321     getCount : function(){
24322         return this.jsonData ? this.jsonData.length : 0;
24323     },
24324
24325 /**
24326  * Returns the JSON object for the specified node(s)
24327  * @param {HTMLElement/Array} node The node or an array of nodes
24328  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24329  * you get the JSON object for the node
24330  */
24331     getNodeData : function(node){
24332         if(node instanceof Array){
24333             var data = [];
24334             for(var i = 0, len = node.length; i < len; i++){
24335                 data.push(this.getNodeData(node[i]));
24336             }
24337             return data;
24338         }
24339         return this.jsonData[this.indexOf(node)] || null;
24340     },
24341
24342     beforeRender : function(){
24343         this.snapshot = this.jsonData;
24344         if(this.sortInfo){
24345             this.sort.apply(this, this.sortInfo);
24346         }
24347         this.fireEvent("beforerender", this, this.jsonData);
24348     },
24349
24350     onLoad : function(el, o){
24351         this.fireEvent("load", this, this.jsonData, o);
24352     },
24353
24354     onLoadException : function(el, o){
24355         this.fireEvent("loadexception", this, o);
24356     },
24357
24358 /**
24359  * Filter the data by a specific property.
24360  * @param {String} property A property on your JSON objects
24361  * @param {String/RegExp} value Either string that the property values
24362  * should start with, or a RegExp to test against the property
24363  */
24364     filter : function(property, value){
24365         if(this.jsonData){
24366             var data = [];
24367             var ss = this.snapshot;
24368             if(typeof value == "string"){
24369                 var vlen = value.length;
24370                 if(vlen == 0){
24371                     this.clearFilter();
24372                     return;
24373                 }
24374                 value = value.toLowerCase();
24375                 for(var i = 0, len = ss.length; i < len; i++){
24376                     var o = ss[i];
24377                     if(o[property].substr(0, vlen).toLowerCase() == value){
24378                         data.push(o);
24379                     }
24380                 }
24381             } else if(value.exec){ // regex?
24382                 for(var i = 0, len = ss.length; i < len; i++){
24383                     var o = ss[i];
24384                     if(value.test(o[property])){
24385                         data.push(o);
24386                     }
24387                 }
24388             } else{
24389                 return;
24390             }
24391             this.jsonData = data;
24392             this.refresh();
24393         }
24394     },
24395
24396 /**
24397  * Filter by a function. The passed function will be called with each
24398  * object in the current dataset. If the function returns true the value is kept,
24399  * otherwise it is filtered.
24400  * @param {Function} fn
24401  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24402  */
24403     filterBy : function(fn, scope){
24404         if(this.jsonData){
24405             var data = [];
24406             var ss = this.snapshot;
24407             for(var i = 0, len = ss.length; i < len; i++){
24408                 var o = ss[i];
24409                 if(fn.call(scope || this, o)){
24410                     data.push(o);
24411                 }
24412             }
24413             this.jsonData = data;
24414             this.refresh();
24415         }
24416     },
24417
24418 /**
24419  * Clears the current filter.
24420  */
24421     clearFilter : function(){
24422         if(this.snapshot && this.jsonData != this.snapshot){
24423             this.jsonData = this.snapshot;
24424             this.refresh();
24425         }
24426     },
24427
24428
24429 /**
24430  * Sorts the data for this view and refreshes it.
24431  * @param {String} property A property on your JSON objects to sort on
24432  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24433  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24434  */
24435     sort : function(property, dir, sortType){
24436         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24437         if(this.jsonData){
24438             var p = property;
24439             var dsc = dir && dir.toLowerCase() == "desc";
24440             var f = function(o1, o2){
24441                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24442                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24443                 ;
24444                 if(v1 < v2){
24445                     return dsc ? +1 : -1;
24446                 } else if(v1 > v2){
24447                     return dsc ? -1 : +1;
24448                 } else{
24449                     return 0;
24450                 }
24451             };
24452             this.jsonData.sort(f);
24453             this.refresh();
24454             if(this.jsonData != this.snapshot){
24455                 this.snapshot.sort(f);
24456             }
24457         }
24458     }
24459 });/*
24460  * Based on:
24461  * Ext JS Library 1.1.1
24462  * Copyright(c) 2006-2007, Ext JS, LLC.
24463  *
24464  * Originally Released Under LGPL - original licence link has changed is not relivant.
24465  *
24466  * Fork - LGPL
24467  * <script type="text/javascript">
24468  */
24469  
24470
24471 /**
24472  * @class Roo.ColorPalette
24473  * @extends Roo.Component
24474  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24475  * Here's an example of typical usage:
24476  * <pre><code>
24477 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24478 cp.render('my-div');
24479
24480 cp.on('select', function(palette, selColor){
24481     // do something with selColor
24482 });
24483 </code></pre>
24484  * @constructor
24485  * Create a new ColorPalette
24486  * @param {Object} config The config object
24487  */
24488 Roo.ColorPalette = function(config){
24489     Roo.ColorPalette.superclass.constructor.call(this, config);
24490     this.addEvents({
24491         /**
24492              * @event select
24493              * Fires when a color is selected
24494              * @param {ColorPalette} this
24495              * @param {String} color The 6-digit color hex code (without the # symbol)
24496              */
24497         select: true
24498     });
24499
24500     if(this.handler){
24501         this.on("select", this.handler, this.scope, true);
24502     }
24503 };
24504 Roo.extend(Roo.ColorPalette, Roo.Component, {
24505     /**
24506      * @cfg {String} itemCls
24507      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24508      */
24509     itemCls : "x-color-palette",
24510     /**
24511      * @cfg {String} value
24512      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24513      * the hex codes are case-sensitive.
24514      */
24515     value : null,
24516     clickEvent:'click',
24517     // private
24518     ctype: "Roo.ColorPalette",
24519
24520     /**
24521      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24522      */
24523     allowReselect : false,
24524
24525     /**
24526      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24527      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24528      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24529      * of colors with the width setting until the box is symmetrical.</p>
24530      * <p>You can override individual colors if needed:</p>
24531      * <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors[0] = "FF0000";  // change the first box to red
24534 </code></pre>
24535
24536 Or you can provide a custom array of your own for complete control:
24537 <pre><code>
24538 var cp = new Roo.ColorPalette();
24539 cp.colors = ["000000", "993300", "333300"];
24540 </code></pre>
24541      * @type Array
24542      */
24543     colors : [
24544         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24545         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24546         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24547         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24548         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24549     ],
24550
24551     // private
24552     onRender : function(container, position){
24553         var t = new Roo.MasterTemplate(
24554             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24555         );
24556         var c = this.colors;
24557         for(var i = 0, len = c.length; i < len; i++){
24558             t.add([c[i]]);
24559         }
24560         var el = document.createElement("div");
24561         el.className = this.itemCls;
24562         t.overwrite(el);
24563         container.dom.insertBefore(el, position);
24564         this.el = Roo.get(el);
24565         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24566         if(this.clickEvent != 'click'){
24567             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24568         }
24569     },
24570
24571     // private
24572     afterRender : function(){
24573         Roo.ColorPalette.superclass.afterRender.call(this);
24574         if(this.value){
24575             var s = this.value;
24576             this.value = null;
24577             this.select(s);
24578         }
24579     },
24580
24581     // private
24582     handleClick : function(e, t){
24583         e.preventDefault();
24584         if(!this.disabled){
24585             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24586             this.select(c.toUpperCase());
24587         }
24588     },
24589
24590     /**
24591      * Selects the specified color in the palette (fires the select event)
24592      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24593      */
24594     select : function(color){
24595         color = color.replace("#", "");
24596         if(color != this.value || this.allowReselect){
24597             var el = this.el;
24598             if(this.value){
24599                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24600             }
24601             el.child("a.color-"+color).addClass("x-color-palette-sel");
24602             this.value = color;
24603             this.fireEvent("select", this, color);
24604         }
24605     }
24606 });/*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616  
24617 /**
24618  * @class Roo.DatePicker
24619  * @extends Roo.Component
24620  * Simple date picker class.
24621  * @constructor
24622  * Create a new DatePicker
24623  * @param {Object} config The config object
24624  */
24625 Roo.DatePicker = function(config){
24626     Roo.DatePicker.superclass.constructor.call(this, config);
24627
24628     this.value = config && config.value ?
24629                  config.value.clearTime() : new Date().clearTime();
24630
24631     this.addEvents({
24632         /**
24633              * @event select
24634              * Fires when a date is selected
24635              * @param {DatePicker} this
24636              * @param {Date} date The selected date
24637              */
24638         'select': true,
24639         /**
24640              * @event monthchange
24641              * Fires when the displayed month changes 
24642              * @param {DatePicker} this
24643              * @param {Date} date The selected month
24644              */
24645         'monthchange': true
24646     });
24647
24648     if(this.handler){
24649         this.on("select", this.handler,  this.scope || this);
24650     }
24651     // build the disabledDatesRE
24652     if(!this.disabledDatesRE && this.disabledDates){
24653         var dd = this.disabledDates;
24654         var re = "(?:";
24655         for(var i = 0; i < dd.length; i++){
24656             re += dd[i];
24657             if(i != dd.length-1) re += "|";
24658         }
24659         this.disabledDatesRE = new RegExp(re + ")");
24660     }
24661 };
24662
24663 Roo.extend(Roo.DatePicker, Roo.Component, {
24664     /**
24665      * @cfg {String} todayText
24666      * The text to display on the button that selects the current date (defaults to "Today")
24667      */
24668     todayText : "Today",
24669     /**
24670      * @cfg {String} okText
24671      * The text to display on the ok button
24672      */
24673     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24674     /**
24675      * @cfg {String} cancelText
24676      * The text to display on the cancel button
24677      */
24678     cancelText : "Cancel",
24679     /**
24680      * @cfg {String} todayTip
24681      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24682      */
24683     todayTip : "{0} (Spacebar)",
24684     /**
24685      * @cfg {Date} minDate
24686      * Minimum allowable date (JavaScript date object, defaults to null)
24687      */
24688     minDate : null,
24689     /**
24690      * @cfg {Date} maxDate
24691      * Maximum allowable date (JavaScript date object, defaults to null)
24692      */
24693     maxDate : null,
24694     /**
24695      * @cfg {String} minText
24696      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24697      */
24698     minText : "This date is before the minimum date",
24699     /**
24700      * @cfg {String} maxText
24701      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24702      */
24703     maxText : "This date is after the maximum date",
24704     /**
24705      * @cfg {String} format
24706      * The default date format string which can be overriden for localization support.  The format must be
24707      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24708      */
24709     format : "m/d/y",
24710     /**
24711      * @cfg {Array} disabledDays
24712      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24713      */
24714     disabledDays : null,
24715     /**
24716      * @cfg {String} disabledDaysText
24717      * The tooltip to display when the date falls on a disabled day (defaults to "")
24718      */
24719     disabledDaysText : "",
24720     /**
24721      * @cfg {RegExp} disabledDatesRE
24722      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24723      */
24724     disabledDatesRE : null,
24725     /**
24726      * @cfg {String} disabledDatesText
24727      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24728      */
24729     disabledDatesText : "",
24730     /**
24731      * @cfg {Boolean} constrainToViewport
24732      * True to constrain the date picker to the viewport (defaults to true)
24733      */
24734     constrainToViewport : true,
24735     /**
24736      * @cfg {Array} monthNames
24737      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24738      */
24739     monthNames : Date.monthNames,
24740     /**
24741      * @cfg {Array} dayNames
24742      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24743      */
24744     dayNames : Date.dayNames,
24745     /**
24746      * @cfg {String} nextText
24747      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24748      */
24749     nextText: 'Next Month (Control+Right)',
24750     /**
24751      * @cfg {String} prevText
24752      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24753      */
24754     prevText: 'Previous Month (Control+Left)',
24755     /**
24756      * @cfg {String} monthYearText
24757      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24758      */
24759     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24760     /**
24761      * @cfg {Number} startDay
24762      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24763      */
24764     startDay : 0,
24765     /**
24766      * @cfg {Bool} showClear
24767      * Show a clear button (usefull for date form elements that can be blank.)
24768      */
24769     
24770     showClear: false,
24771     
24772     /**
24773      * Sets the value of the date field
24774      * @param {Date} value The date to set
24775      */
24776     setValue : function(value){
24777         var old = this.value;
24778         this.value = value.clearTime(true);
24779         if(this.el){
24780             this.update(this.value);
24781         }
24782     },
24783
24784     /**
24785      * Gets the current selected value of the date field
24786      * @return {Date} The selected date
24787      */
24788     getValue : function(){
24789         return this.value;
24790     },
24791
24792     // private
24793     focus : function(){
24794         if(this.el){
24795             this.update(this.activeDate);
24796         }
24797     },
24798
24799     // private
24800     onRender : function(container, position){
24801         var m = [
24802              '<table cellspacing="0">',
24803                 '<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>',
24804                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24805         var dn = this.dayNames;
24806         for(var i = 0; i < 7; i++){
24807             var d = this.startDay+i;
24808             if(d > 6){
24809                 d = d-7;
24810             }
24811             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24812         }
24813         m[m.length] = "</tr></thead><tbody><tr>";
24814         for(var i = 0; i < 42; i++) {
24815             if(i % 7 == 0 && i != 0){
24816                 m[m.length] = "</tr><tr>";
24817             }
24818             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24819         }
24820         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24821             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24822
24823         var el = document.createElement("div");
24824         el.className = "x-date-picker";
24825         el.innerHTML = m.join("");
24826
24827         container.dom.insertBefore(el, position);
24828
24829         this.el = Roo.get(el);
24830         this.eventEl = Roo.get(el.firstChild);
24831
24832         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24833             handler: this.showPrevMonth,
24834             scope: this,
24835             preventDefault:true,
24836             stopDefault:true
24837         });
24838
24839         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24840             handler: this.showNextMonth,
24841             scope: this,
24842             preventDefault:true,
24843             stopDefault:true
24844         });
24845
24846         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24847
24848         this.monthPicker = this.el.down('div.x-date-mp');
24849         this.monthPicker.enableDisplayMode('block');
24850         
24851         var kn = new Roo.KeyNav(this.eventEl, {
24852             "left" : function(e){
24853                 e.ctrlKey ?
24854                     this.showPrevMonth() :
24855                     this.update(this.activeDate.add("d", -1));
24856             },
24857
24858             "right" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextMonth() :
24861                     this.update(this.activeDate.add("d", 1));
24862             },
24863
24864             "up" : function(e){
24865                 e.ctrlKey ?
24866                     this.showNextYear() :
24867                     this.update(this.activeDate.add("d", -7));
24868             },
24869
24870             "down" : function(e){
24871                 e.ctrlKey ?
24872                     this.showPrevYear() :
24873                     this.update(this.activeDate.add("d", 7));
24874             },
24875
24876             "pageUp" : function(e){
24877                 this.showNextMonth();
24878             },
24879
24880             "pageDown" : function(e){
24881                 this.showPrevMonth();
24882             },
24883
24884             "enter" : function(e){
24885                 e.stopPropagation();
24886                 return true;
24887             },
24888
24889             scope : this
24890         });
24891
24892         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24893
24894         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24895
24896         this.el.unselectable();
24897         
24898         this.cells = this.el.select("table.x-date-inner tbody td");
24899         this.textNodes = this.el.query("table.x-date-inner tbody span");
24900
24901         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24902             text: "&#160;",
24903             tooltip: this.monthYearText
24904         });
24905
24906         this.mbtn.on('click', this.showMonthPicker, this);
24907         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24908
24909
24910         var today = (new Date()).dateFormat(this.format);
24911         
24912         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24913         if (this.showClear) {
24914             baseTb.add( new Roo.Toolbar.Fill());
24915         }
24916         baseTb.add({
24917             text: String.format(this.todayText, today),
24918             tooltip: String.format(this.todayTip, today),
24919             handler: this.selectToday,
24920             scope: this
24921         });
24922         
24923         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24924             
24925         //});
24926         if (this.showClear) {
24927             
24928             baseTb.add( new Roo.Toolbar.Fill());
24929             baseTb.add({
24930                 text: '&#160;',
24931                 cls: 'x-btn-icon x-btn-clear',
24932                 handler: function() {
24933                     //this.value = '';
24934                     this.fireEvent("select", this, '');
24935                 },
24936                 scope: this
24937             });
24938         }
24939         
24940         
24941         if(Roo.isIE){
24942             this.el.repaint();
24943         }
24944         this.update(this.value);
24945     },
24946
24947     createMonthPicker : function(){
24948         if(!this.monthPicker.dom.firstChild){
24949             var buf = ['<table border="0" cellspacing="0">'];
24950             for(var i = 0; i < 6; i++){
24951                 buf.push(
24952                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24953                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24954                     i == 0 ?
24955                     '<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>' :
24956                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24957                 );
24958             }
24959             buf.push(
24960                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24961                     this.okText,
24962                     '</button><button type="button" class="x-date-mp-cancel">',
24963                     this.cancelText,
24964                     '</button></td></tr>',
24965                 '</table>'
24966             );
24967             this.monthPicker.update(buf.join(''));
24968             this.monthPicker.on('click', this.onMonthClick, this);
24969             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24970
24971             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24972             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24973
24974             this.mpMonths.each(function(m, a, i){
24975                 i += 1;
24976                 if((i%2) == 0){
24977                     m.dom.xmonth = 5 + Math.round(i * .5);
24978                 }else{
24979                     m.dom.xmonth = Math.round((i-1) * .5);
24980                 }
24981             });
24982         }
24983     },
24984
24985     showMonthPicker : function(){
24986         this.createMonthPicker();
24987         var size = this.el.getSize();
24988         this.monthPicker.setSize(size);
24989         this.monthPicker.child('table').setSize(size);
24990
24991         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24992         this.updateMPMonth(this.mpSelMonth);
24993         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24994         this.updateMPYear(this.mpSelYear);
24995
24996         this.monthPicker.slideIn('t', {duration:.2});
24997     },
24998
24999     updateMPYear : function(y){
25000         this.mpyear = y;
25001         var ys = this.mpYears.elements;
25002         for(var i = 1; i <= 10; i++){
25003             var td = ys[i-1], y2;
25004             if((i%2) == 0){
25005                 y2 = y + Math.round(i * .5);
25006                 td.firstChild.innerHTML = y2;
25007                 td.xyear = y2;
25008             }else{
25009                 y2 = y - (5-Math.round(i * .5));
25010                 td.firstChild.innerHTML = y2;
25011                 td.xyear = y2;
25012             }
25013             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         }
25015     },
25016
25017     updateMPMonth : function(sm){
25018         this.mpMonths.each(function(m, a, i){
25019             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25020         });
25021     },
25022
25023     selectMPMonth: function(m){
25024         
25025     },
25026
25027     onMonthClick : function(e, t){
25028         e.stopEvent();
25029         var el = new Roo.Element(t), pn;
25030         if(el.is('button.x-date-mp-cancel')){
25031             this.hideMonthPicker();
25032         }
25033         else if(el.is('button.x-date-mp-ok')){
25034             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25035             this.hideMonthPicker();
25036         }
25037         else if(pn = el.up('td.x-date-mp-month', 2)){
25038             this.mpMonths.removeClass('x-date-mp-sel');
25039             pn.addClass('x-date-mp-sel');
25040             this.mpSelMonth = pn.dom.xmonth;
25041         }
25042         else if(pn = el.up('td.x-date-mp-year', 2)){
25043             this.mpYears.removeClass('x-date-mp-sel');
25044             pn.addClass('x-date-mp-sel');
25045             this.mpSelYear = pn.dom.xyear;
25046         }
25047         else if(el.is('a.x-date-mp-prev')){
25048             this.updateMPYear(this.mpyear-10);
25049         }
25050         else if(el.is('a.x-date-mp-next')){
25051             this.updateMPYear(this.mpyear+10);
25052         }
25053     },
25054
25055     onMonthDblClick : function(e, t){
25056         e.stopEvent();
25057         var el = new Roo.Element(t), pn;
25058         if(pn = el.up('td.x-date-mp-month', 2)){
25059             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25060             this.hideMonthPicker();
25061         }
25062         else if(pn = el.up('td.x-date-mp-year', 2)){
25063             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25064             this.hideMonthPicker();
25065         }
25066     },
25067
25068     hideMonthPicker : function(disableAnim){
25069         if(this.monthPicker){
25070             if(disableAnim === true){
25071                 this.monthPicker.hide();
25072             }else{
25073                 this.monthPicker.slideOut('t', {duration:.2});
25074             }
25075         }
25076     },
25077
25078     // private
25079     showPrevMonth : function(e){
25080         this.update(this.activeDate.add("mo", -1));
25081     },
25082
25083     // private
25084     showNextMonth : function(e){
25085         this.update(this.activeDate.add("mo", 1));
25086     },
25087
25088     // private
25089     showPrevYear : function(){
25090         this.update(this.activeDate.add("y", -1));
25091     },
25092
25093     // private
25094     showNextYear : function(){
25095         this.update(this.activeDate.add("y", 1));
25096     },
25097
25098     // private
25099     handleMouseWheel : function(e){
25100         var delta = e.getWheelDelta();
25101         if(delta > 0){
25102             this.showPrevMonth();
25103             e.stopEvent();
25104         } else if(delta < 0){
25105             this.showNextMonth();
25106             e.stopEvent();
25107         }
25108     },
25109
25110     // private
25111     handleDateClick : function(e, t){
25112         e.stopEvent();
25113         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25114             this.setValue(new Date(t.dateValue));
25115             this.fireEvent("select", this, this.value);
25116         }
25117     },
25118
25119     // private
25120     selectToday : function(){
25121         this.setValue(new Date().clearTime());
25122         this.fireEvent("select", this, this.value);
25123     },
25124
25125     // private
25126     update : function(date)
25127     {
25128         var vd = this.activeDate;
25129         this.activeDate = date;
25130         if(vd && this.el){
25131             var t = date.getTime();
25132             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25133                 this.cells.removeClass("x-date-selected");
25134                 this.cells.each(function(c){
25135                    if(c.dom.firstChild.dateValue == t){
25136                        c.addClass("x-date-selected");
25137                        setTimeout(function(){
25138                             try{c.dom.firstChild.focus();}catch(e){}
25139                        }, 50);
25140                        return false;
25141                    }
25142                 });
25143                 return;
25144             }
25145         }
25146         
25147         var days = date.getDaysInMonth();
25148         var firstOfMonth = date.getFirstDateOfMonth();
25149         var startingPos = firstOfMonth.getDay()-this.startDay;
25150
25151         if(startingPos <= this.startDay){
25152             startingPos += 7;
25153         }
25154
25155         var pm = date.add("mo", -1);
25156         var prevStart = pm.getDaysInMonth()-startingPos;
25157
25158         var cells = this.cells.elements;
25159         var textEls = this.textNodes;
25160         days += startingPos;
25161
25162         // convert everything to numbers so it's fast
25163         var day = 86400000;
25164         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25165         var today = new Date().clearTime().getTime();
25166         var sel = date.clearTime().getTime();
25167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25169         var ddMatch = this.disabledDatesRE;
25170         var ddText = this.disabledDatesText;
25171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25172         var ddaysText = this.disabledDaysText;
25173         var format = this.format;
25174
25175         var setCellClass = function(cal, cell){
25176             cell.title = "";
25177             var t = d.getTime();
25178             cell.firstChild.dateValue = t;
25179             if(t == today){
25180                 cell.className += " x-date-today";
25181                 cell.title = cal.todayText;
25182             }
25183             if(t == sel){
25184                 cell.className += " x-date-selected";
25185                 setTimeout(function(){
25186                     try{cell.firstChild.focus();}catch(e){}
25187                 }, 50);
25188             }
25189             // disabling
25190             if(t < min) {
25191                 cell.className = " x-date-disabled";
25192                 cell.title = cal.minText;
25193                 return;
25194             }
25195             if(t > max) {
25196                 cell.className = " x-date-disabled";
25197                 cell.title = cal.maxText;
25198                 return;
25199             }
25200             if(ddays){
25201                 if(ddays.indexOf(d.getDay()) != -1){
25202                     cell.title = ddaysText;
25203                     cell.className = " x-date-disabled";
25204                 }
25205             }
25206             if(ddMatch && format){
25207                 var fvalue = d.dateFormat(format);
25208                 if(ddMatch.test(fvalue)){
25209                     cell.title = ddText.replace("%0", fvalue);
25210                     cell.className = " x-date-disabled";
25211                 }
25212             }
25213         };
25214
25215         var i = 0;
25216         for(; i < startingPos; i++) {
25217             textEls[i].innerHTML = (++prevStart);
25218             d.setDate(d.getDate()+1);
25219             cells[i].className = "x-date-prevday";
25220             setCellClass(this, cells[i]);
25221         }
25222         for(; i < days; i++){
25223             intDay = i - startingPos + 1;
25224             textEls[i].innerHTML = (intDay);
25225             d.setDate(d.getDate()+1);
25226             cells[i].className = "x-date-active";
25227             setCellClass(this, cells[i]);
25228         }
25229         var extraDays = 0;
25230         for(; i < 42; i++) {
25231              textEls[i].innerHTML = (++extraDays);
25232              d.setDate(d.getDate()+1);
25233              cells[i].className = "x-date-nextday";
25234              setCellClass(this, cells[i]);
25235         }
25236
25237         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25238         this.fireEvent('monthchange', this, date);
25239         
25240         if(!this.internalRender){
25241             var main = this.el.dom.firstChild;
25242             var w = main.offsetWidth;
25243             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25244             Roo.fly(main).setWidth(w);
25245             this.internalRender = true;
25246             // opera does not respect the auto grow header center column
25247             // then, after it gets a width opera refuses to recalculate
25248             // without a second pass
25249             if(Roo.isOpera && !this.secondPass){
25250                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25251                 this.secondPass = true;
25252                 this.update.defer(10, this, [date]);
25253             }
25254         }
25255         
25256         
25257     }
25258 });        /*
25259  * Based on:
25260  * Ext JS Library 1.1.1
25261  * Copyright(c) 2006-2007, Ext JS, LLC.
25262  *
25263  * Originally Released Under LGPL - original licence link has changed is not relivant.
25264  *
25265  * Fork - LGPL
25266  * <script type="text/javascript">
25267  */
25268 /**
25269  * @class Roo.TabPanel
25270  * @extends Roo.util.Observable
25271  * A lightweight tab container.
25272  * <br><br>
25273  * Usage:
25274  * <pre><code>
25275 // basic tabs 1, built from existing content
25276 var tabs = new Roo.TabPanel("tabs1");
25277 tabs.addTab("script", "View Script");
25278 tabs.addTab("markup", "View Markup");
25279 tabs.activate("script");
25280
25281 // more advanced tabs, built from javascript
25282 var jtabs = new Roo.TabPanel("jtabs");
25283 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25284
25285 // set up the UpdateManager
25286 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25287 var updater = tab2.getUpdateManager();
25288 updater.setDefaultUrl("ajax1.htm");
25289 tab2.on('activate', updater.refresh, updater, true);
25290
25291 // Use setUrl for Ajax loading
25292 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25293 tab3.setUrl("ajax2.htm", null, true);
25294
25295 // Disabled tab
25296 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25297 tab4.disable();
25298
25299 jtabs.activate("jtabs-1");
25300  * </code></pre>
25301  * @constructor
25302  * Create a new TabPanel.
25303  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25304  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25305  */
25306 Roo.TabPanel = function(container, config){
25307     /**
25308     * The container element for this TabPanel.
25309     * @type Roo.Element
25310     */
25311     this.el = Roo.get(container, true);
25312     if(config){
25313         if(typeof config == "boolean"){
25314             this.tabPosition = config ? "bottom" : "top";
25315         }else{
25316             Roo.apply(this, config);
25317         }
25318     }
25319     if(this.tabPosition == "bottom"){
25320         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25321         this.el.addClass("x-tabs-bottom");
25322     }
25323     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25324     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25325     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25326     if(Roo.isIE){
25327         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25328     }
25329     if(this.tabPosition != "bottom"){
25330         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25331          * @type Roo.Element
25332          */
25333         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25334         this.el.addClass("x-tabs-top");
25335     }
25336     this.items = [];
25337
25338     this.bodyEl.setStyle("position", "relative");
25339
25340     this.active = null;
25341     this.activateDelegate = this.activate.createDelegate(this);
25342
25343     this.addEvents({
25344         /**
25345          * @event tabchange
25346          * Fires when the active tab changes
25347          * @param {Roo.TabPanel} this
25348          * @param {Roo.TabPanelItem} activePanel The new active tab
25349          */
25350         "tabchange": true,
25351         /**
25352          * @event beforetabchange
25353          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25354          * @param {Roo.TabPanel} this
25355          * @param {Object} e Set cancel to true on this object to cancel the tab change
25356          * @param {Roo.TabPanelItem} tab The tab being changed to
25357          */
25358         "beforetabchange" : true
25359     });
25360
25361     Roo.EventManager.onWindowResize(this.onResize, this);
25362     this.cpad = this.el.getPadding("lr");
25363     this.hiddenCount = 0;
25364
25365
25366     // toolbar on the tabbar support...
25367     if (this.toolbar) {
25368         var tcfg = this.toolbar;
25369         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25370         this.toolbar = new Roo.Toolbar(tcfg);
25371         if (Roo.isSafari) {
25372             var tbl = tcfg.container.child('table', true);
25373             tbl.setAttribute('width', '100%');
25374         }
25375         
25376     }
25377    
25378
25379
25380     Roo.TabPanel.superclass.constructor.call(this);
25381 };
25382
25383 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25384     /*
25385      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25386      */
25387     tabPosition : "top",
25388     /*
25389      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25390      */
25391     currentTabWidth : 0,
25392     /*
25393      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25394      */
25395     minTabWidth : 40,
25396     /*
25397      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25398      */
25399     maxTabWidth : 250,
25400     /*
25401      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25402      */
25403     preferredTabWidth : 175,
25404     /*
25405      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25406      */
25407     resizeTabs : false,
25408     /*
25409      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25410      */
25411     monitorResize : true,
25412     /*
25413      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25414      */
25415     toolbar : false,
25416
25417     /**
25418      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25419      * @param {String} id The id of the div to use <b>or create</b>
25420      * @param {String} text The text for the tab
25421      * @param {String} content (optional) Content to put in the TabPanelItem body
25422      * @param {Boolean} closable (optional) True to create a close icon on the tab
25423      * @return {Roo.TabPanelItem} The created TabPanelItem
25424      */
25425     addTab : function(id, text, content, closable){
25426         var item = new Roo.TabPanelItem(this, id, text, closable);
25427         this.addTabItem(item);
25428         if(content){
25429             item.setContent(content);
25430         }
25431         return item;
25432     },
25433
25434     /**
25435      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25436      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25437      * @return {Roo.TabPanelItem}
25438      */
25439     getTab : function(id){
25440         return this.items[id];
25441     },
25442
25443     /**
25444      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25445      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25446      */
25447     hideTab : function(id){
25448         var t = this.items[id];
25449         if(!t.isHidden()){
25450            t.setHidden(true);
25451            this.hiddenCount++;
25452            this.autoSizeTabs();
25453         }
25454     },
25455
25456     /**
25457      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25458      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25459      */
25460     unhideTab : function(id){
25461         var t = this.items[id];
25462         if(t.isHidden()){
25463            t.setHidden(false);
25464            this.hiddenCount--;
25465            this.autoSizeTabs();
25466         }
25467     },
25468
25469     /**
25470      * Adds an existing {@link Roo.TabPanelItem}.
25471      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25472      */
25473     addTabItem : function(item){
25474         this.items[item.id] = item;
25475         this.items.push(item);
25476         if(this.resizeTabs){
25477            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25478            this.autoSizeTabs();
25479         }else{
25480             item.autoSize();
25481         }
25482     },
25483
25484     /**
25485      * Removes a {@link Roo.TabPanelItem}.
25486      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25487      */
25488     removeTab : function(id){
25489         var items = this.items;
25490         var tab = items[id];
25491         if(!tab) { return; }
25492         var index = items.indexOf(tab);
25493         if(this.active == tab && items.length > 1){
25494             var newTab = this.getNextAvailable(index);
25495             if(newTab) {
25496                 newTab.activate();
25497             }
25498         }
25499         this.stripEl.dom.removeChild(tab.pnode.dom);
25500         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25501             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25502         }
25503         items.splice(index, 1);
25504         delete this.items[tab.id];
25505         tab.fireEvent("close", tab);
25506         tab.purgeListeners();
25507         this.autoSizeTabs();
25508     },
25509
25510     getNextAvailable : function(start){
25511         var items = this.items;
25512         var index = start;
25513         // look for a next tab that will slide over to
25514         // replace the one being removed
25515         while(index < items.length){
25516             var item = items[++index];
25517             if(item && !item.isHidden()){
25518                 return item;
25519             }
25520         }
25521         // if one isn't found select the previous tab (on the left)
25522         index = start;
25523         while(index >= 0){
25524             var item = items[--index];
25525             if(item && !item.isHidden()){
25526                 return item;
25527             }
25528         }
25529         return null;
25530     },
25531
25532     /**
25533      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25534      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25535      */
25536     disableTab : function(id){
25537         var tab = this.items[id];
25538         if(tab && this.active != tab){
25539             tab.disable();
25540         }
25541     },
25542
25543     /**
25544      * Enables a {@link Roo.TabPanelItem} that is disabled.
25545      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25546      */
25547     enableTab : function(id){
25548         var tab = this.items[id];
25549         tab.enable();
25550     },
25551
25552     /**
25553      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25554      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25555      * @return {Roo.TabPanelItem} The TabPanelItem.
25556      */
25557     activate : function(id){
25558         var tab = this.items[id];
25559         if(!tab){
25560             return null;
25561         }
25562         if(tab == this.active || tab.disabled){
25563             return tab;
25564         }
25565         var e = {};
25566         this.fireEvent("beforetabchange", this, e, tab);
25567         if(e.cancel !== true && !tab.disabled){
25568             if(this.active){
25569                 this.active.hide();
25570             }
25571             this.active = this.items[id];
25572             this.active.show();
25573             this.fireEvent("tabchange", this, this.active);
25574         }
25575         return tab;
25576     },
25577
25578     /**
25579      * Gets the active {@link Roo.TabPanelItem}.
25580      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25581      */
25582     getActiveTab : function(){
25583         return this.active;
25584     },
25585
25586     /**
25587      * Updates the tab body element to fit the height of the container element
25588      * for overflow scrolling
25589      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25590      */
25591     syncHeight : function(targetHeight){
25592         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25593         var bm = this.bodyEl.getMargins();
25594         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25595         this.bodyEl.setHeight(newHeight);
25596         return newHeight;
25597     },
25598
25599     onResize : function(){
25600         if(this.monitorResize){
25601             this.autoSizeTabs();
25602         }
25603     },
25604
25605     /**
25606      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25607      */
25608     beginUpdate : function(){
25609         this.updating = true;
25610     },
25611
25612     /**
25613      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25614      */
25615     endUpdate : function(){
25616         this.updating = false;
25617         this.autoSizeTabs();
25618     },
25619
25620     /**
25621      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25622      */
25623     autoSizeTabs : function(){
25624         var count = this.items.length;
25625         var vcount = count - this.hiddenCount;
25626         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25627         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25628         var availWidth = Math.floor(w / vcount);
25629         var b = this.stripBody;
25630         if(b.getWidth() > w){
25631             var tabs = this.items;
25632             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25633             if(availWidth < this.minTabWidth){
25634                 /*if(!this.sleft){    // incomplete scrolling code
25635                     this.createScrollButtons();
25636                 }
25637                 this.showScroll();
25638                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25639             }
25640         }else{
25641             if(this.currentTabWidth < this.preferredTabWidth){
25642                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25643             }
25644         }
25645     },
25646
25647     /**
25648      * Returns the number of tabs in this TabPanel.
25649      * @return {Number}
25650      */
25651      getCount : function(){
25652          return this.items.length;
25653      },
25654
25655     /**
25656      * Resizes all the tabs to the passed width
25657      * @param {Number} The new width
25658      */
25659     setTabWidth : function(width){
25660         this.currentTabWidth = width;
25661         for(var i = 0, len = this.items.length; i < len; i++) {
25662                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25663         }
25664     },
25665
25666     /**
25667      * Destroys this TabPanel
25668      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25669      */
25670     destroy : function(removeEl){
25671         Roo.EventManager.removeResizeListener(this.onResize, this);
25672         for(var i = 0, len = this.items.length; i < len; i++){
25673             this.items[i].purgeListeners();
25674         }
25675         if(removeEl === true){
25676             this.el.update("");
25677             this.el.remove();
25678         }
25679     }
25680 });
25681
25682 /**
25683  * @class Roo.TabPanelItem
25684  * @extends Roo.util.Observable
25685  * Represents an individual item (tab plus body) in a TabPanel.
25686  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25687  * @param {String} id The id of this TabPanelItem
25688  * @param {String} text The text for the tab of this TabPanelItem
25689  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25690  */
25691 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25692     /**
25693      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25694      * @type Roo.TabPanel
25695      */
25696     this.tabPanel = tabPanel;
25697     /**
25698      * The id for this TabPanelItem
25699      * @type String
25700      */
25701     this.id = id;
25702     /** @private */
25703     this.disabled = false;
25704     /** @private */
25705     this.text = text;
25706     /** @private */
25707     this.loaded = false;
25708     this.closable = closable;
25709
25710     /**
25711      * The body element for this TabPanelItem.
25712      * @type Roo.Element
25713      */
25714     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25715     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25716     this.bodyEl.setStyle("display", "block");
25717     this.bodyEl.setStyle("zoom", "1");
25718     this.hideAction();
25719
25720     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25721     /** @private */
25722     this.el = Roo.get(els.el, true);
25723     this.inner = Roo.get(els.inner, true);
25724     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25725     this.pnode = Roo.get(els.el.parentNode, true);
25726     this.el.on("mousedown", this.onTabMouseDown, this);
25727     this.el.on("click", this.onTabClick, this);
25728     /** @private */
25729     if(closable){
25730         var c = Roo.get(els.close, true);
25731         c.dom.title = this.closeText;
25732         c.addClassOnOver("close-over");
25733         c.on("click", this.closeClick, this);
25734      }
25735
25736     this.addEvents({
25737          /**
25738          * @event activate
25739          * Fires when this tab becomes the active tab.
25740          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25741          * @param {Roo.TabPanelItem} this
25742          */
25743         "activate": true,
25744         /**
25745          * @event beforeclose
25746          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25747          * @param {Roo.TabPanelItem} this
25748          * @param {Object} e Set cancel to true on this object to cancel the close.
25749          */
25750         "beforeclose": true,
25751         /**
25752          * @event close
25753          * Fires when this tab is closed.
25754          * @param {Roo.TabPanelItem} this
25755          */
25756          "close": true,
25757         /**
25758          * @event deactivate
25759          * Fires when this tab is no longer the active tab.
25760          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25761          * @param {Roo.TabPanelItem} this
25762          */
25763          "deactivate" : true
25764     });
25765     this.hidden = false;
25766
25767     Roo.TabPanelItem.superclass.constructor.call(this);
25768 };
25769
25770 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25771     purgeListeners : function(){
25772        Roo.util.Observable.prototype.purgeListeners.call(this);
25773        this.el.removeAllListeners();
25774     },
25775     /**
25776      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25777      */
25778     show : function(){
25779         this.pnode.addClass("on");
25780         this.showAction();
25781         if(Roo.isOpera){
25782             this.tabPanel.stripWrap.repaint();
25783         }
25784         this.fireEvent("activate", this.tabPanel, this);
25785     },
25786
25787     /**
25788      * Returns true if this tab is the active tab.
25789      * @return {Boolean}
25790      */
25791     isActive : function(){
25792         return this.tabPanel.getActiveTab() == this;
25793     },
25794
25795     /**
25796      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25797      */
25798     hide : function(){
25799         this.pnode.removeClass("on");
25800         this.hideAction();
25801         this.fireEvent("deactivate", this.tabPanel, this);
25802     },
25803
25804     hideAction : function(){
25805         this.bodyEl.hide();
25806         this.bodyEl.setStyle("position", "absolute");
25807         this.bodyEl.setLeft("-20000px");
25808         this.bodyEl.setTop("-20000px");
25809     },
25810
25811     showAction : function(){
25812         this.bodyEl.setStyle("position", "relative");
25813         this.bodyEl.setTop("");
25814         this.bodyEl.setLeft("");
25815         this.bodyEl.show();
25816     },
25817
25818     /**
25819      * Set the tooltip for the tab.
25820      * @param {String} tooltip The tab's tooltip
25821      */
25822     setTooltip : function(text){
25823         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25824             this.textEl.dom.qtip = text;
25825             this.textEl.dom.removeAttribute('title');
25826         }else{
25827             this.textEl.dom.title = text;
25828         }
25829     },
25830
25831     onTabClick : function(e){
25832         e.preventDefault();
25833         this.tabPanel.activate(this.id);
25834     },
25835
25836     onTabMouseDown : function(e){
25837         e.preventDefault();
25838         this.tabPanel.activate(this.id);
25839     },
25840
25841     getWidth : function(){
25842         return this.inner.getWidth();
25843     },
25844
25845     setWidth : function(width){
25846         var iwidth = width - this.pnode.getPadding("lr");
25847         this.inner.setWidth(iwidth);
25848         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25849         this.pnode.setWidth(width);
25850     },
25851
25852     /**
25853      * Show or hide the tab
25854      * @param {Boolean} hidden True to hide or false to show.
25855      */
25856     setHidden : function(hidden){
25857         this.hidden = hidden;
25858         this.pnode.setStyle("display", hidden ? "none" : "");
25859     },
25860
25861     /**
25862      * Returns true if this tab is "hidden"
25863      * @return {Boolean}
25864      */
25865     isHidden : function(){
25866         return this.hidden;
25867     },
25868
25869     /**
25870      * Returns the text for this tab
25871      * @return {String}
25872      */
25873     getText : function(){
25874         return this.text;
25875     },
25876
25877     autoSize : function(){
25878         //this.el.beginMeasure();
25879         this.textEl.setWidth(1);
25880         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25881         //this.el.endMeasure();
25882     },
25883
25884     /**
25885      * Sets the text for the tab (Note: this also sets the tooltip text)
25886      * @param {String} text The tab's text and tooltip
25887      */
25888     setText : function(text){
25889         this.text = text;
25890         this.textEl.update(text);
25891         this.setTooltip(text);
25892         if(!this.tabPanel.resizeTabs){
25893             this.autoSize();
25894         }
25895     },
25896     /**
25897      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25898      */
25899     activate : function(){
25900         this.tabPanel.activate(this.id);
25901     },
25902
25903     /**
25904      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25905      */
25906     disable : function(){
25907         if(this.tabPanel.active != this){
25908             this.disabled = true;
25909             this.pnode.addClass("disabled");
25910         }
25911     },
25912
25913     /**
25914      * Enables this TabPanelItem if it was previously disabled.
25915      */
25916     enable : function(){
25917         this.disabled = false;
25918         this.pnode.removeClass("disabled");
25919     },
25920
25921     /**
25922      * Sets the content for this TabPanelItem.
25923      * @param {String} content The content
25924      * @param {Boolean} loadScripts true to look for and load scripts
25925      */
25926     setContent : function(content, loadScripts){
25927         this.bodyEl.update(content, loadScripts);
25928     },
25929
25930     /**
25931      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25932      * @return {Roo.UpdateManager} The UpdateManager
25933      */
25934     getUpdateManager : function(){
25935         return this.bodyEl.getUpdateManager();
25936     },
25937
25938     /**
25939      * Set a URL to be used to load the content for this TabPanelItem.
25940      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25941      * @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)
25942      * @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)
25943      * @return {Roo.UpdateManager} The UpdateManager
25944      */
25945     setUrl : function(url, params, loadOnce){
25946         if(this.refreshDelegate){
25947             this.un('activate', this.refreshDelegate);
25948         }
25949         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25950         this.on("activate", this.refreshDelegate);
25951         return this.bodyEl.getUpdateManager();
25952     },
25953
25954     /** @private */
25955     _handleRefresh : function(url, params, loadOnce){
25956         if(!loadOnce || !this.loaded){
25957             var updater = this.bodyEl.getUpdateManager();
25958             updater.update(url, params, this._setLoaded.createDelegate(this));
25959         }
25960     },
25961
25962     /**
25963      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25964      *   Will fail silently if the setUrl method has not been called.
25965      *   This does not activate the panel, just updates its content.
25966      */
25967     refresh : function(){
25968         if(this.refreshDelegate){
25969            this.loaded = false;
25970            this.refreshDelegate();
25971         }
25972     },
25973
25974     /** @private */
25975     _setLoaded : function(){
25976         this.loaded = true;
25977     },
25978
25979     /** @private */
25980     closeClick : function(e){
25981         var o = {};
25982         e.stopEvent();
25983         this.fireEvent("beforeclose", this, o);
25984         if(o.cancel !== true){
25985             this.tabPanel.removeTab(this.id);
25986         }
25987     },
25988     /**
25989      * The text displayed in the tooltip for the close icon.
25990      * @type String
25991      */
25992     closeText : "Close this tab"
25993 });
25994
25995 /** @private */
25996 Roo.TabPanel.prototype.createStrip = function(container){
25997     var strip = document.createElement("div");
25998     strip.className = "x-tabs-wrap";
25999     container.appendChild(strip);
26000     return strip;
26001 };
26002 /** @private */
26003 Roo.TabPanel.prototype.createStripList = function(strip){
26004     // div wrapper for retard IE
26005     // returns the "tr" element.
26006     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26007         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26008         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26009     return strip.firstChild.firstChild.firstChild.firstChild;
26010 };
26011 /** @private */
26012 Roo.TabPanel.prototype.createBody = function(container){
26013     var body = document.createElement("div");
26014     Roo.id(body, "tab-body");
26015     Roo.fly(body).addClass("x-tabs-body");
26016     container.appendChild(body);
26017     return body;
26018 };
26019 /** @private */
26020 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26021     var body = Roo.getDom(id);
26022     if(!body){
26023         body = document.createElement("div");
26024         body.id = id;
26025     }
26026     Roo.fly(body).addClass("x-tabs-item-body");
26027     bodyEl.insertBefore(body, bodyEl.firstChild);
26028     return body;
26029 };
26030 /** @private */
26031 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26032     var td = document.createElement("td");
26033     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26034     //stripEl.appendChild(td);
26035     if(closable){
26036         td.className = "x-tabs-closable";
26037         if(!this.closeTpl){
26038             this.closeTpl = new Roo.Template(
26039                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26040                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26041                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26042             );
26043         }
26044         var el = this.closeTpl.overwrite(td, {"text": text});
26045         var close = el.getElementsByTagName("div")[0];
26046         var inner = el.getElementsByTagName("em")[0];
26047         return {"el": el, "close": close, "inner": inner};
26048     } else {
26049         if(!this.tabTpl){
26050             this.tabTpl = new Roo.Template(
26051                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26052                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26053             );
26054         }
26055         var el = this.tabTpl.overwrite(td, {"text": text});
26056         var inner = el.getElementsByTagName("em")[0];
26057         return {"el": el, "inner": inner};
26058     }
26059 };/*
26060  * Based on:
26061  * Ext JS Library 1.1.1
26062  * Copyright(c) 2006-2007, Ext JS, LLC.
26063  *
26064  * Originally Released Under LGPL - original licence link has changed is not relivant.
26065  *
26066  * Fork - LGPL
26067  * <script type="text/javascript">
26068  */
26069
26070 /**
26071  * @class Roo.Button
26072  * @extends Roo.util.Observable
26073  * Simple Button class
26074  * @cfg {String} text The button text
26075  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26076  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26077  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26078  * @cfg {Object} scope The scope of the handler
26079  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26080  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26081  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26082  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26083  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26084  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26085    applies if enableToggle = true)
26086  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26087  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26088   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26089  * @constructor
26090  * Create a new button
26091  * @param {Object} config The config object
26092  */
26093 Roo.Button = function(renderTo, config)
26094 {
26095     if (!config) {
26096         config = renderTo;
26097         renderTo = config.renderTo || false;
26098     }
26099     
26100     Roo.apply(this, config);
26101     this.addEvents({
26102         /**
26103              * @event click
26104              * Fires when this button is clicked
26105              * @param {Button} this
26106              * @param {EventObject} e The click event
26107              */
26108             "click" : true,
26109         /**
26110              * @event toggle
26111              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26112              * @param {Button} this
26113              * @param {Boolean} pressed
26114              */
26115             "toggle" : true,
26116         /**
26117              * @event mouseover
26118              * Fires when the mouse hovers over the button
26119              * @param {Button} this
26120              * @param {Event} e The event object
26121              */
26122         'mouseover' : true,
26123         /**
26124              * @event mouseout
26125              * Fires when the mouse exits the button
26126              * @param {Button} this
26127              * @param {Event} e The event object
26128              */
26129         'mouseout': true,
26130          /**
26131              * @event render
26132              * Fires when the button is rendered
26133              * @param {Button} this
26134              */
26135         'render': true
26136     });
26137     if(this.menu){
26138         this.menu = Roo.menu.MenuMgr.get(this.menu);
26139     }
26140     // register listeners first!!  - so render can be captured..
26141     Roo.util.Observable.call(this);
26142     if(renderTo){
26143         this.render(renderTo);
26144     }
26145     
26146   
26147 };
26148
26149 Roo.extend(Roo.Button, Roo.util.Observable, {
26150     /**
26151      * 
26152      */
26153     
26154     /**
26155      * Read-only. True if this button is hidden
26156      * @type Boolean
26157      */
26158     hidden : false,
26159     /**
26160      * Read-only. True if this button is disabled
26161      * @type Boolean
26162      */
26163     disabled : false,
26164     /**
26165      * Read-only. True if this button is pressed (only if enableToggle = true)
26166      * @type Boolean
26167      */
26168     pressed : false,
26169
26170     /**
26171      * @cfg {Number} tabIndex 
26172      * The DOM tabIndex for this button (defaults to undefined)
26173      */
26174     tabIndex : undefined,
26175
26176     /**
26177      * @cfg {Boolean} enableToggle
26178      * True to enable pressed/not pressed toggling (defaults to false)
26179      */
26180     enableToggle: false,
26181     /**
26182      * @cfg {Mixed} menu
26183      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26184      */
26185     menu : undefined,
26186     /**
26187      * @cfg {String} menuAlign
26188      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26189      */
26190     menuAlign : "tl-bl?",
26191
26192     /**
26193      * @cfg {String} iconCls
26194      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26195      */
26196     iconCls : undefined,
26197     /**
26198      * @cfg {String} type
26199      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26200      */
26201     type : 'button',
26202
26203     // private
26204     menuClassTarget: 'tr',
26205
26206     /**
26207      * @cfg {String} clickEvent
26208      * The type of event to map to the button's event handler (defaults to 'click')
26209      */
26210     clickEvent : 'click',
26211
26212     /**
26213      * @cfg {Boolean} handleMouseEvents
26214      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26215      */
26216     handleMouseEvents : true,
26217
26218     /**
26219      * @cfg {String} tooltipType
26220      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26221      */
26222     tooltipType : 'qtip',
26223
26224     /**
26225      * @cfg {String} cls
26226      * A CSS class to apply to the button's main element.
26227      */
26228     
26229     /**
26230      * @cfg {Roo.Template} template (Optional)
26231      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26232      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26233      * require code modifications if required elements (e.g. a button) aren't present.
26234      */
26235
26236     // private
26237     render : function(renderTo){
26238         var btn;
26239         if(this.hideParent){
26240             this.parentEl = Roo.get(renderTo);
26241         }
26242         if(!this.dhconfig){
26243             if(!this.template){
26244                 if(!Roo.Button.buttonTemplate){
26245                     // hideous table template
26246                     Roo.Button.buttonTemplate = new Roo.Template(
26247                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26248                         '<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>',
26249                         "</tr></tbody></table>");
26250                 }
26251                 this.template = Roo.Button.buttonTemplate;
26252             }
26253             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26254             var btnEl = btn.child("button:first");
26255             btnEl.on('focus', this.onFocus, this);
26256             btnEl.on('blur', this.onBlur, this);
26257             if(this.cls){
26258                 btn.addClass(this.cls);
26259             }
26260             if(this.icon){
26261                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26262             }
26263             if(this.iconCls){
26264                 btnEl.addClass(this.iconCls);
26265                 if(!this.cls){
26266                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26267                 }
26268             }
26269             if(this.tabIndex !== undefined){
26270                 btnEl.dom.tabIndex = this.tabIndex;
26271             }
26272             if(this.tooltip){
26273                 if(typeof this.tooltip == 'object'){
26274                     Roo.QuickTips.tips(Roo.apply({
26275                           target: btnEl.id
26276                     }, this.tooltip));
26277                 } else {
26278                     btnEl.dom[this.tooltipType] = this.tooltip;
26279                 }
26280             }
26281         }else{
26282             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26283         }
26284         this.el = btn;
26285         if(this.id){
26286             this.el.dom.id = this.el.id = this.id;
26287         }
26288         if(this.menu){
26289             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26290             this.menu.on("show", this.onMenuShow, this);
26291             this.menu.on("hide", this.onMenuHide, this);
26292         }
26293         btn.addClass("x-btn");
26294         if(Roo.isIE && !Roo.isIE7){
26295             this.autoWidth.defer(1, this);
26296         }else{
26297             this.autoWidth();
26298         }
26299         if(this.handleMouseEvents){
26300             btn.on("mouseover", this.onMouseOver, this);
26301             btn.on("mouseout", this.onMouseOut, this);
26302             btn.on("mousedown", this.onMouseDown, this);
26303         }
26304         btn.on(this.clickEvent, this.onClick, this);
26305         //btn.on("mouseup", this.onMouseUp, this);
26306         if(this.hidden){
26307             this.hide();
26308         }
26309         if(this.disabled){
26310             this.disable();
26311         }
26312         Roo.ButtonToggleMgr.register(this);
26313         if(this.pressed){
26314             this.el.addClass("x-btn-pressed");
26315         }
26316         if(this.repeat){
26317             var repeater = new Roo.util.ClickRepeater(btn,
26318                 typeof this.repeat == "object" ? this.repeat : {}
26319             );
26320             repeater.on("click", this.onClick,  this);
26321         }
26322         
26323         this.fireEvent('render', this);
26324         
26325     },
26326     /**
26327      * Returns the button's underlying element
26328      * @return {Roo.Element} The element
26329      */
26330     getEl : function(){
26331         return this.el;  
26332     },
26333     
26334     /**
26335      * Destroys this Button and removes any listeners.
26336      */
26337     destroy : function(){
26338         Roo.ButtonToggleMgr.unregister(this);
26339         this.el.removeAllListeners();
26340         this.purgeListeners();
26341         this.el.remove();
26342     },
26343
26344     // private
26345     autoWidth : function(){
26346         if(this.el){
26347             this.el.setWidth("auto");
26348             if(Roo.isIE7 && Roo.isStrict){
26349                 var ib = this.el.child('button');
26350                 if(ib && ib.getWidth() > 20){
26351                     ib.clip();
26352                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26353                 }
26354             }
26355             if(this.minWidth){
26356                 if(this.hidden){
26357                     this.el.beginMeasure();
26358                 }
26359                 if(this.el.getWidth() < this.minWidth){
26360                     this.el.setWidth(this.minWidth);
26361                 }
26362                 if(this.hidden){
26363                     this.el.endMeasure();
26364                 }
26365             }
26366         }
26367     },
26368
26369     /**
26370      * Assigns this button's click handler
26371      * @param {Function} handler The function to call when the button is clicked
26372      * @param {Object} scope (optional) Scope for the function passed in
26373      */
26374     setHandler : function(handler, scope){
26375         this.handler = handler;
26376         this.scope = scope;  
26377     },
26378     
26379     /**
26380      * Sets this button's text
26381      * @param {String} text The button text
26382      */
26383     setText : function(text){
26384         this.text = text;
26385         if(this.el){
26386             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26387         }
26388         this.autoWidth();
26389     },
26390     
26391     /**
26392      * Gets the text for this button
26393      * @return {String} The button text
26394      */
26395     getText : function(){
26396         return this.text;  
26397     },
26398     
26399     /**
26400      * Show this button
26401      */
26402     show: function(){
26403         this.hidden = false;
26404         if(this.el){
26405             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26406         }
26407     },
26408     
26409     /**
26410      * Hide this button
26411      */
26412     hide: function(){
26413         this.hidden = true;
26414         if(this.el){
26415             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26416         }
26417     },
26418     
26419     /**
26420      * Convenience function for boolean show/hide
26421      * @param {Boolean} visible True to show, false to hide
26422      */
26423     setVisible: function(visible){
26424         if(visible) {
26425             this.show();
26426         }else{
26427             this.hide();
26428         }
26429     },
26430     
26431     /**
26432      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26433      * @param {Boolean} state (optional) Force a particular state
26434      */
26435     toggle : function(state){
26436         state = state === undefined ? !this.pressed : state;
26437         if(state != this.pressed){
26438             if(state){
26439                 this.el.addClass("x-btn-pressed");
26440                 this.pressed = true;
26441                 this.fireEvent("toggle", this, true);
26442             }else{
26443                 this.el.removeClass("x-btn-pressed");
26444                 this.pressed = false;
26445                 this.fireEvent("toggle", this, false);
26446             }
26447             if(this.toggleHandler){
26448                 this.toggleHandler.call(this.scope || this, this, state);
26449             }
26450         }
26451     },
26452     
26453     /**
26454      * Focus the button
26455      */
26456     focus : function(){
26457         this.el.child('button:first').focus();
26458     },
26459     
26460     /**
26461      * Disable this button
26462      */
26463     disable : function(){
26464         if(this.el){
26465             this.el.addClass("x-btn-disabled");
26466         }
26467         this.disabled = true;
26468     },
26469     
26470     /**
26471      * Enable this button
26472      */
26473     enable : function(){
26474         if(this.el){
26475             this.el.removeClass("x-btn-disabled");
26476         }
26477         this.disabled = false;
26478     },
26479
26480     /**
26481      * Convenience function for boolean enable/disable
26482      * @param {Boolean} enabled True to enable, false to disable
26483      */
26484     setDisabled : function(v){
26485         this[v !== true ? "enable" : "disable"]();
26486     },
26487
26488     // private
26489     onClick : function(e){
26490         if(e){
26491             e.preventDefault();
26492         }
26493         if(e.button != 0){
26494             return;
26495         }
26496         if(!this.disabled){
26497             if(this.enableToggle){
26498                 this.toggle();
26499             }
26500             if(this.menu && !this.menu.isVisible()){
26501                 this.menu.show(this.el, this.menuAlign);
26502             }
26503             this.fireEvent("click", this, e);
26504             if(this.handler){
26505                 this.el.removeClass("x-btn-over");
26506                 this.handler.call(this.scope || this, this, e);
26507             }
26508         }
26509     },
26510     // private
26511     onMouseOver : function(e){
26512         if(!this.disabled){
26513             this.el.addClass("x-btn-over");
26514             this.fireEvent('mouseover', this, e);
26515         }
26516     },
26517     // private
26518     onMouseOut : function(e){
26519         if(!e.within(this.el,  true)){
26520             this.el.removeClass("x-btn-over");
26521             this.fireEvent('mouseout', this, e);
26522         }
26523     },
26524     // private
26525     onFocus : function(e){
26526         if(!this.disabled){
26527             this.el.addClass("x-btn-focus");
26528         }
26529     },
26530     // private
26531     onBlur : function(e){
26532         this.el.removeClass("x-btn-focus");
26533     },
26534     // private
26535     onMouseDown : function(e){
26536         if(!this.disabled && e.button == 0){
26537             this.el.addClass("x-btn-click");
26538             Roo.get(document).on('mouseup', this.onMouseUp, this);
26539         }
26540     },
26541     // private
26542     onMouseUp : function(e){
26543         if(e.button == 0){
26544             this.el.removeClass("x-btn-click");
26545             Roo.get(document).un('mouseup', this.onMouseUp, this);
26546         }
26547     },
26548     // private
26549     onMenuShow : function(e){
26550         this.el.addClass("x-btn-menu-active");
26551     },
26552     // private
26553     onMenuHide : function(e){
26554         this.el.removeClass("x-btn-menu-active");
26555     }   
26556 });
26557
26558 // Private utility class used by Button
26559 Roo.ButtonToggleMgr = function(){
26560    var groups = {};
26561    
26562    function toggleGroup(btn, state){
26563        if(state){
26564            var g = groups[btn.toggleGroup];
26565            for(var i = 0, l = g.length; i < l; i++){
26566                if(g[i] != btn){
26567                    g[i].toggle(false);
26568                }
26569            }
26570        }
26571    }
26572    
26573    return {
26574        register : function(btn){
26575            if(!btn.toggleGroup){
26576                return;
26577            }
26578            var g = groups[btn.toggleGroup];
26579            if(!g){
26580                g = groups[btn.toggleGroup] = [];
26581            }
26582            g.push(btn);
26583            btn.on("toggle", toggleGroup);
26584        },
26585        
26586        unregister : function(btn){
26587            if(!btn.toggleGroup){
26588                return;
26589            }
26590            var g = groups[btn.toggleGroup];
26591            if(g){
26592                g.remove(btn);
26593                btn.un("toggle", toggleGroup);
26594            }
26595        }
26596    };
26597 }();/*
26598  * Based on:
26599  * Ext JS Library 1.1.1
26600  * Copyright(c) 2006-2007, Ext JS, LLC.
26601  *
26602  * Originally Released Under LGPL - original licence link has changed is not relivant.
26603  *
26604  * Fork - LGPL
26605  * <script type="text/javascript">
26606  */
26607  
26608 /**
26609  * @class Roo.SplitButton
26610  * @extends Roo.Button
26611  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26612  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26613  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26614  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26615  * @cfg {String} arrowTooltip The title attribute of the arrow
26616  * @constructor
26617  * Create a new menu button
26618  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26619  * @param {Object} config The config object
26620  */
26621 Roo.SplitButton = function(renderTo, config){
26622     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26623     /**
26624      * @event arrowclick
26625      * Fires when this button's arrow is clicked
26626      * @param {SplitButton} this
26627      * @param {EventObject} e The click event
26628      */
26629     this.addEvents({"arrowclick":true});
26630 };
26631
26632 Roo.extend(Roo.SplitButton, Roo.Button, {
26633     render : function(renderTo){
26634         // this is one sweet looking template!
26635         var tpl = new Roo.Template(
26636             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26637             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26638             '<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>',
26639             "</tbody></table></td><td>",
26640             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26641             '<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>',
26642             "</tbody></table></td></tr></table>"
26643         );
26644         var btn = tpl.append(renderTo, [this.text, this.type], true);
26645         var btnEl = btn.child("button");
26646         if(this.cls){
26647             btn.addClass(this.cls);
26648         }
26649         if(this.icon){
26650             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26651         }
26652         if(this.iconCls){
26653             btnEl.addClass(this.iconCls);
26654             if(!this.cls){
26655                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26656             }
26657         }
26658         this.el = btn;
26659         if(this.handleMouseEvents){
26660             btn.on("mouseover", this.onMouseOver, this);
26661             btn.on("mouseout", this.onMouseOut, this);
26662             btn.on("mousedown", this.onMouseDown, this);
26663             btn.on("mouseup", this.onMouseUp, this);
26664         }
26665         btn.on(this.clickEvent, this.onClick, this);
26666         if(this.tooltip){
26667             if(typeof this.tooltip == 'object'){
26668                 Roo.QuickTips.tips(Roo.apply({
26669                       target: btnEl.id
26670                 }, this.tooltip));
26671             } else {
26672                 btnEl.dom[this.tooltipType] = this.tooltip;
26673             }
26674         }
26675         if(this.arrowTooltip){
26676             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26677         }
26678         if(this.hidden){
26679             this.hide();
26680         }
26681         if(this.disabled){
26682             this.disable();
26683         }
26684         if(this.pressed){
26685             this.el.addClass("x-btn-pressed");
26686         }
26687         if(Roo.isIE && !Roo.isIE7){
26688             this.autoWidth.defer(1, this);
26689         }else{
26690             this.autoWidth();
26691         }
26692         if(this.menu){
26693             this.menu.on("show", this.onMenuShow, this);
26694             this.menu.on("hide", this.onMenuHide, this);
26695         }
26696         this.fireEvent('render', this);
26697     },
26698
26699     // private
26700     autoWidth : function(){
26701         if(this.el){
26702             var tbl = this.el.child("table:first");
26703             var tbl2 = this.el.child("table:last");
26704             this.el.setWidth("auto");
26705             tbl.setWidth("auto");
26706             if(Roo.isIE7 && Roo.isStrict){
26707                 var ib = this.el.child('button:first');
26708                 if(ib && ib.getWidth() > 20){
26709                     ib.clip();
26710                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26711                 }
26712             }
26713             if(this.minWidth){
26714                 if(this.hidden){
26715                     this.el.beginMeasure();
26716                 }
26717                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26718                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26719                 }
26720                 if(this.hidden){
26721                     this.el.endMeasure();
26722                 }
26723             }
26724             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26725         } 
26726     },
26727     /**
26728      * Sets this button's click handler
26729      * @param {Function} handler The function to call when the button is clicked
26730      * @param {Object} scope (optional) Scope for the function passed above
26731      */
26732     setHandler : function(handler, scope){
26733         this.handler = handler;
26734         this.scope = scope;  
26735     },
26736     
26737     /**
26738      * Sets this button's arrow click handler
26739      * @param {Function} handler The function to call when the arrow is clicked
26740      * @param {Object} scope (optional) Scope for the function passed above
26741      */
26742     setArrowHandler : function(handler, scope){
26743         this.arrowHandler = handler;
26744         this.scope = scope;  
26745     },
26746     
26747     /**
26748      * Focus the button
26749      */
26750     focus : function(){
26751         if(this.el){
26752             this.el.child("button:first").focus();
26753         }
26754     },
26755
26756     // private
26757     onClick : function(e){
26758         e.preventDefault();
26759         if(!this.disabled){
26760             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26761                 if(this.menu && !this.menu.isVisible()){
26762                     this.menu.show(this.el, this.menuAlign);
26763                 }
26764                 this.fireEvent("arrowclick", this, e);
26765                 if(this.arrowHandler){
26766                     this.arrowHandler.call(this.scope || this, this, e);
26767                 }
26768             }else{
26769                 this.fireEvent("click", this, e);
26770                 if(this.handler){
26771                     this.handler.call(this.scope || this, this, e);
26772                 }
26773             }
26774         }
26775     },
26776     // private
26777     onMouseDown : function(e){
26778         if(!this.disabled){
26779             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26780         }
26781     },
26782     // private
26783     onMouseUp : function(e){
26784         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26785     }   
26786 });
26787
26788
26789 // backwards compat
26790 Roo.MenuButton = Roo.SplitButton;/*
26791  * Based on:
26792  * Ext JS Library 1.1.1
26793  * Copyright(c) 2006-2007, Ext JS, LLC.
26794  *
26795  * Originally Released Under LGPL - original licence link has changed is not relivant.
26796  *
26797  * Fork - LGPL
26798  * <script type="text/javascript">
26799  */
26800
26801 /**
26802  * @class Roo.Toolbar
26803  * Basic Toolbar class.
26804  * @constructor
26805  * Creates a new Toolbar
26806  * @param {Object} container The config object
26807  */ 
26808 Roo.Toolbar = function(container, buttons, config)
26809 {
26810     /// old consturctor format still supported..
26811     if(container instanceof Array){ // omit the container for later rendering
26812         buttons = container;
26813         config = buttons;
26814         container = null;
26815     }
26816     if (typeof(container) == 'object' && container.xtype) {
26817         config = container;
26818         container = config.container;
26819         buttons = config.buttons || []; // not really - use items!!
26820     }
26821     var xitems = [];
26822     if (config && config.items) {
26823         xitems = config.items;
26824         delete config.items;
26825     }
26826     Roo.apply(this, config);
26827     this.buttons = buttons;
26828     
26829     if(container){
26830         this.render(container);
26831     }
26832     this.xitems = xitems;
26833     Roo.each(xitems, function(b) {
26834         this.add(b);
26835     }, this);
26836     
26837 };
26838
26839 Roo.Toolbar.prototype = {
26840     /**
26841      * @cfg {Array} items
26842      * array of button configs or elements to add (will be converted to a MixedCollection)
26843      */
26844     
26845     /**
26846      * @cfg {String/HTMLElement/Element} container
26847      * The id or element that will contain the toolbar
26848      */
26849     // private
26850     render : function(ct){
26851         this.el = Roo.get(ct);
26852         if(this.cls){
26853             this.el.addClass(this.cls);
26854         }
26855         // using a table allows for vertical alignment
26856         // 100% width is needed by Safari...
26857         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26858         this.tr = this.el.child("tr", true);
26859         var autoId = 0;
26860         this.items = new Roo.util.MixedCollection(false, function(o){
26861             return o.id || ("item" + (++autoId));
26862         });
26863         if(this.buttons){
26864             this.add.apply(this, this.buttons);
26865             delete this.buttons;
26866         }
26867     },
26868
26869     /**
26870      * Adds element(s) to the toolbar -- this function takes a variable number of 
26871      * arguments of mixed type and adds them to the toolbar.
26872      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26873      * <ul>
26874      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26875      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26876      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26877      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26878      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26879      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26880      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26881      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26882      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26883      * </ul>
26884      * @param {Mixed} arg2
26885      * @param {Mixed} etc.
26886      */
26887     add : function(){
26888         var a = arguments, l = a.length;
26889         for(var i = 0; i < l; i++){
26890             this._add(a[i]);
26891         }
26892     },
26893     // private..
26894     _add : function(el) {
26895         
26896         if (el.xtype) {
26897             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26898         }
26899         
26900         if (el.applyTo){ // some kind of form field
26901             return this.addField(el);
26902         } 
26903         if (el.render){ // some kind of Toolbar.Item
26904             return this.addItem(el);
26905         }
26906         if (typeof el == "string"){ // string
26907             if(el == "separator" || el == "-"){
26908                 return this.addSeparator();
26909             }
26910             if (el == " "){
26911                 return this.addSpacer();
26912             }
26913             if(el == "->"){
26914                 return this.addFill();
26915             }
26916             return this.addText(el);
26917             
26918         }
26919         if(el.tagName){ // element
26920             return this.addElement(el);
26921         }
26922         if(typeof el == "object"){ // must be button config?
26923             return this.addButton(el);
26924         }
26925         // and now what?!?!
26926         return false;
26927         
26928     },
26929     
26930     /**
26931      * Add an Xtype element
26932      * @param {Object} xtype Xtype Object
26933      * @return {Object} created Object
26934      */
26935     addxtype : function(e){
26936         return this.add(e);  
26937     },
26938     
26939     /**
26940      * Returns the Element for this toolbar.
26941      * @return {Roo.Element}
26942      */
26943     getEl : function(){
26944         return this.el;  
26945     },
26946     
26947     /**
26948      * Adds a separator
26949      * @return {Roo.Toolbar.Item} The separator item
26950      */
26951     addSeparator : function(){
26952         return this.addItem(new Roo.Toolbar.Separator());
26953     },
26954
26955     /**
26956      * Adds a spacer element
26957      * @return {Roo.Toolbar.Spacer} The spacer item
26958      */
26959     addSpacer : function(){
26960         return this.addItem(new Roo.Toolbar.Spacer());
26961     },
26962
26963     /**
26964      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26965      * @return {Roo.Toolbar.Fill} The fill item
26966      */
26967     addFill : function(){
26968         return this.addItem(new Roo.Toolbar.Fill());
26969     },
26970
26971     /**
26972      * Adds any standard HTML element to the toolbar
26973      * @param {String/HTMLElement/Element} el The element or id of the element to add
26974      * @return {Roo.Toolbar.Item} The element's item
26975      */
26976     addElement : function(el){
26977         return this.addItem(new Roo.Toolbar.Item(el));
26978     },
26979     /**
26980      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26981      * @type Roo.util.MixedCollection  
26982      */
26983     items : false,
26984      
26985     /**
26986      * Adds any Toolbar.Item or subclass
26987      * @param {Roo.Toolbar.Item} item
26988      * @return {Roo.Toolbar.Item} The item
26989      */
26990     addItem : function(item){
26991         var td = this.nextBlock();
26992         item.render(td);
26993         this.items.add(item);
26994         return item;
26995     },
26996     
26997     /**
26998      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26999      * @param {Object/Array} config A button config or array of configs
27000      * @return {Roo.Toolbar.Button/Array}
27001      */
27002     addButton : function(config){
27003         if(config instanceof Array){
27004             var buttons = [];
27005             for(var i = 0, len = config.length; i < len; i++) {
27006                 buttons.push(this.addButton(config[i]));
27007             }
27008             return buttons;
27009         }
27010         var b = config;
27011         if(!(config instanceof Roo.Toolbar.Button)){
27012             b = config.split ?
27013                 new Roo.Toolbar.SplitButton(config) :
27014                 new Roo.Toolbar.Button(config);
27015         }
27016         var td = this.nextBlock();
27017         b.render(td);
27018         this.items.add(b);
27019         return b;
27020     },
27021     
27022     /**
27023      * Adds text to the toolbar
27024      * @param {String} text The text to add
27025      * @return {Roo.Toolbar.Item} The element's item
27026      */
27027     addText : function(text){
27028         return this.addItem(new Roo.Toolbar.TextItem(text));
27029     },
27030     
27031     /**
27032      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27033      * @param {Number} index The index where the item is to be inserted
27034      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27035      * @return {Roo.Toolbar.Button/Item}
27036      */
27037     insertButton : function(index, item){
27038         if(item instanceof Array){
27039             var buttons = [];
27040             for(var i = 0, len = item.length; i < len; i++) {
27041                buttons.push(this.insertButton(index + i, item[i]));
27042             }
27043             return buttons;
27044         }
27045         if (!(item instanceof Roo.Toolbar.Button)){
27046            item = new Roo.Toolbar.Button(item);
27047         }
27048         var td = document.createElement("td");
27049         this.tr.insertBefore(td, this.tr.childNodes[index]);
27050         item.render(td);
27051         this.items.insert(index, item);
27052         return item;
27053     },
27054     
27055     /**
27056      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27057      * @param {Object} config
27058      * @return {Roo.Toolbar.Item} The element's item
27059      */
27060     addDom : function(config, returnEl){
27061         var td = this.nextBlock();
27062         Roo.DomHelper.overwrite(td, config);
27063         var ti = new Roo.Toolbar.Item(td.firstChild);
27064         ti.render(td);
27065         this.items.add(ti);
27066         return ti;
27067     },
27068
27069     /**
27070      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27071      * @type Roo.util.MixedCollection  
27072      */
27073     fields : false,
27074     
27075     /**
27076      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27077      * Note: the field should not have been rendered yet. For a field that has already been
27078      * rendered, use {@link #addElement}.
27079      * @param {Roo.form.Field} field
27080      * @return {Roo.ToolbarItem}
27081      */
27082      
27083       
27084     addField : function(field) {
27085         if (!this.fields) {
27086             var autoId = 0;
27087             this.fields = new Roo.util.MixedCollection(false, function(o){
27088                 return o.id || ("item" + (++autoId));
27089             });
27090
27091         }
27092         
27093         var td = this.nextBlock();
27094         field.render(td);
27095         var ti = new Roo.Toolbar.Item(td.firstChild);
27096         ti.render(td);
27097         this.items.add(ti);
27098         this.fields.add(field);
27099         return ti;
27100     },
27101     /**
27102      * Hide the toolbar
27103      * @method hide
27104      */
27105      
27106       
27107     hide : function()
27108     {
27109         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27110         this.el.child('div').hide();
27111     },
27112     /**
27113      * Show the toolbar
27114      * @method show
27115      */
27116     show : function()
27117     {
27118         this.el.child('div').show();
27119     },
27120       
27121     // private
27122     nextBlock : function(){
27123         var td = document.createElement("td");
27124         this.tr.appendChild(td);
27125         return td;
27126     },
27127
27128     // private
27129     destroy : function(){
27130         if(this.items){ // rendered?
27131             Roo.destroy.apply(Roo, this.items.items);
27132         }
27133         if(this.fields){ // rendered?
27134             Roo.destroy.apply(Roo, this.fields.items);
27135         }
27136         Roo.Element.uncache(this.el, this.tr);
27137     }
27138 };
27139
27140 /**
27141  * @class Roo.Toolbar.Item
27142  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27143  * @constructor
27144  * Creates a new Item
27145  * @param {HTMLElement} el 
27146  */
27147 Roo.Toolbar.Item = function(el){
27148     this.el = Roo.getDom(el);
27149     this.id = Roo.id(this.el);
27150     this.hidden = false;
27151 };
27152
27153 Roo.Toolbar.Item.prototype = {
27154     
27155     /**
27156      * Get this item's HTML Element
27157      * @return {HTMLElement}
27158      */
27159     getEl : function(){
27160        return this.el;  
27161     },
27162
27163     // private
27164     render : function(td){
27165         this.td = td;
27166         td.appendChild(this.el);
27167     },
27168     
27169     /**
27170      * Removes and destroys this item.
27171      */
27172     destroy : function(){
27173         this.td.parentNode.removeChild(this.td);
27174     },
27175     
27176     /**
27177      * Shows this item.
27178      */
27179     show: function(){
27180         this.hidden = false;
27181         this.td.style.display = "";
27182     },
27183     
27184     /**
27185      * Hides this item.
27186      */
27187     hide: function(){
27188         this.hidden = true;
27189         this.td.style.display = "none";
27190     },
27191     
27192     /**
27193      * Convenience function for boolean show/hide.
27194      * @param {Boolean} visible true to show/false to hide
27195      */
27196     setVisible: function(visible){
27197         if(visible) {
27198             this.show();
27199         }else{
27200             this.hide();
27201         }
27202     },
27203     
27204     /**
27205      * Try to focus this item.
27206      */
27207     focus : function(){
27208         Roo.fly(this.el).focus();
27209     },
27210     
27211     /**
27212      * Disables this item.
27213      */
27214     disable : function(){
27215         Roo.fly(this.td).addClass("x-item-disabled");
27216         this.disabled = true;
27217         this.el.disabled = true;
27218     },
27219     
27220     /**
27221      * Enables this item.
27222      */
27223     enable : function(){
27224         Roo.fly(this.td).removeClass("x-item-disabled");
27225         this.disabled = false;
27226         this.el.disabled = false;
27227     }
27228 };
27229
27230
27231 /**
27232  * @class Roo.Toolbar.Separator
27233  * @extends Roo.Toolbar.Item
27234  * A simple toolbar separator class
27235  * @constructor
27236  * Creates a new Separator
27237  */
27238 Roo.Toolbar.Separator = function(){
27239     var s = document.createElement("span");
27240     s.className = "ytb-sep";
27241     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27242 };
27243 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27244     enable:Roo.emptyFn,
27245     disable:Roo.emptyFn,
27246     focus:Roo.emptyFn
27247 });
27248
27249 /**
27250  * @class Roo.Toolbar.Spacer
27251  * @extends Roo.Toolbar.Item
27252  * A simple element that adds extra horizontal space to a toolbar.
27253  * @constructor
27254  * Creates a new Spacer
27255  */
27256 Roo.Toolbar.Spacer = function(){
27257     var s = document.createElement("div");
27258     s.className = "ytb-spacer";
27259     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27260 };
27261 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27262     enable:Roo.emptyFn,
27263     disable:Roo.emptyFn,
27264     focus:Roo.emptyFn
27265 });
27266
27267 /**
27268  * @class Roo.Toolbar.Fill
27269  * @extends Roo.Toolbar.Spacer
27270  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27271  * @constructor
27272  * Creates a new Spacer
27273  */
27274 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27275     // private
27276     render : function(td){
27277         td.style.width = '100%';
27278         Roo.Toolbar.Fill.superclass.render.call(this, td);
27279     }
27280 });
27281
27282 /**
27283  * @class Roo.Toolbar.TextItem
27284  * @extends Roo.Toolbar.Item
27285  * A simple class that renders text directly into a toolbar.
27286  * @constructor
27287  * Creates a new TextItem
27288  * @param {String} text
27289  */
27290 Roo.Toolbar.TextItem = function(text){
27291     if (typeof(text) == 'object') {
27292         text = text.text;
27293     }
27294     var s = document.createElement("span");
27295     s.className = "ytb-text";
27296     s.innerHTML = text;
27297     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27298 };
27299 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27300     enable:Roo.emptyFn,
27301     disable:Roo.emptyFn,
27302     focus:Roo.emptyFn
27303 });
27304
27305 /**
27306  * @class Roo.Toolbar.Button
27307  * @extends Roo.Button
27308  * A button that renders into a toolbar.
27309  * @constructor
27310  * Creates a new Button
27311  * @param {Object} config A standard {@link Roo.Button} config object
27312  */
27313 Roo.Toolbar.Button = function(config){
27314     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27315 };
27316 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27317     render : function(td){
27318         this.td = td;
27319         Roo.Toolbar.Button.superclass.render.call(this, td);
27320     },
27321     
27322     /**
27323      * Removes and destroys this button
27324      */
27325     destroy : function(){
27326         Roo.Toolbar.Button.superclass.destroy.call(this);
27327         this.td.parentNode.removeChild(this.td);
27328     },
27329     
27330     /**
27331      * Shows this button
27332      */
27333     show: function(){
27334         this.hidden = false;
27335         this.td.style.display = "";
27336     },
27337     
27338     /**
27339      * Hides this button
27340      */
27341     hide: function(){
27342         this.hidden = true;
27343         this.td.style.display = "none";
27344     },
27345
27346     /**
27347      * Disables this item
27348      */
27349     disable : function(){
27350         Roo.fly(this.td).addClass("x-item-disabled");
27351         this.disabled = true;
27352     },
27353
27354     /**
27355      * Enables this item
27356      */
27357     enable : function(){
27358         Roo.fly(this.td).removeClass("x-item-disabled");
27359         this.disabled = false;
27360     }
27361 });
27362 // backwards compat
27363 Roo.ToolbarButton = Roo.Toolbar.Button;
27364
27365 /**
27366  * @class Roo.Toolbar.SplitButton
27367  * @extends Roo.SplitButton
27368  * A menu button that renders into a toolbar.
27369  * @constructor
27370  * Creates a new SplitButton
27371  * @param {Object} config A standard {@link Roo.SplitButton} config object
27372  */
27373 Roo.Toolbar.SplitButton = function(config){
27374     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27375 };
27376 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27377     render : function(td){
27378         this.td = td;
27379         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27380     },
27381     
27382     /**
27383      * Removes and destroys this button
27384      */
27385     destroy : function(){
27386         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27387         this.td.parentNode.removeChild(this.td);
27388     },
27389     
27390     /**
27391      * Shows this button
27392      */
27393     show: function(){
27394         this.hidden = false;
27395         this.td.style.display = "";
27396     },
27397     
27398     /**
27399      * Hides this button
27400      */
27401     hide: function(){
27402         this.hidden = true;
27403         this.td.style.display = "none";
27404     }
27405 });
27406
27407 // backwards compat
27408 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27409  * Based on:
27410  * Ext JS Library 1.1.1
27411  * Copyright(c) 2006-2007, Ext JS, LLC.
27412  *
27413  * Originally Released Under LGPL - original licence link has changed is not relivant.
27414  *
27415  * Fork - LGPL
27416  * <script type="text/javascript">
27417  */
27418  
27419 /**
27420  * @class Roo.PagingToolbar
27421  * @extends Roo.Toolbar
27422  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27423  * @constructor
27424  * Create a new PagingToolbar
27425  * @param {Object} config The config object
27426  */
27427 Roo.PagingToolbar = function(el, ds, config)
27428 {
27429     // old args format still supported... - xtype is prefered..
27430     if (typeof(el) == 'object' && el.xtype) {
27431         // created from xtype...
27432         config = el;
27433         ds = el.dataSource;
27434         el = config.container;
27435     }
27436     var items = [];
27437     if (config.items) {
27438         items = config.items;
27439         config.items = [];
27440     }
27441     
27442     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27443     this.ds = ds;
27444     this.cursor = 0;
27445     this.renderButtons(this.el);
27446     this.bind(ds);
27447     
27448     // supprot items array.
27449    
27450     Roo.each(items, function(e) {
27451         this.add(Roo.factory(e));
27452     },this);
27453     
27454 };
27455
27456 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27457     /**
27458      * @cfg {Roo.data.Store} dataSource
27459      * The underlying data store providing the paged data
27460      */
27461     /**
27462      * @cfg {String/HTMLElement/Element} container
27463      * container The id or element that will contain the toolbar
27464      */
27465     /**
27466      * @cfg {Boolean} displayInfo
27467      * True to display the displayMsg (defaults to false)
27468      */
27469     /**
27470      * @cfg {Number} pageSize
27471      * The number of records to display per page (defaults to 20)
27472      */
27473     pageSize: 20,
27474     /**
27475      * @cfg {String} displayMsg
27476      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27477      */
27478     displayMsg : 'Displaying {0} - {1} of {2}',
27479     /**
27480      * @cfg {String} emptyMsg
27481      * The message to display when no records are found (defaults to "No data to display")
27482      */
27483     emptyMsg : 'No data to display',
27484     /**
27485      * Customizable piece of the default paging text (defaults to "Page")
27486      * @type String
27487      */
27488     beforePageText : "Page",
27489     /**
27490      * Customizable piece of the default paging text (defaults to "of %0")
27491      * @type String
27492      */
27493     afterPageText : "of {0}",
27494     /**
27495      * Customizable piece of the default paging text (defaults to "First Page")
27496      * @type String
27497      */
27498     firstText : "First Page",
27499     /**
27500      * Customizable piece of the default paging text (defaults to "Previous Page")
27501      * @type String
27502      */
27503     prevText : "Previous Page",
27504     /**
27505      * Customizable piece of the default paging text (defaults to "Next Page")
27506      * @type String
27507      */
27508     nextText : "Next Page",
27509     /**
27510      * Customizable piece of the default paging text (defaults to "Last Page")
27511      * @type String
27512      */
27513     lastText : "Last Page",
27514     /**
27515      * Customizable piece of the default paging text (defaults to "Refresh")
27516      * @type String
27517      */
27518     refreshText : "Refresh",
27519
27520     // private
27521     renderButtons : function(el){
27522         Roo.PagingToolbar.superclass.render.call(this, el);
27523         this.first = this.addButton({
27524             tooltip: this.firstText,
27525             cls: "x-btn-icon x-grid-page-first",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["first"])
27528         });
27529         this.prev = this.addButton({
27530             tooltip: this.prevText,
27531             cls: "x-btn-icon x-grid-page-prev",
27532             disabled: true,
27533             handler: this.onClick.createDelegate(this, ["prev"])
27534         });
27535         //this.addSeparator();
27536         this.add(this.beforePageText);
27537         this.field = Roo.get(this.addDom({
27538            tag: "input",
27539            type: "text",
27540            size: "3",
27541            value: "1",
27542            cls: "x-grid-page-number"
27543         }).el);
27544         this.field.on("keydown", this.onPagingKeydown, this);
27545         this.field.on("focus", function(){this.dom.select();});
27546         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27547         this.field.setHeight(18);
27548         //this.addSeparator();
27549         this.next = this.addButton({
27550             tooltip: this.nextText,
27551             cls: "x-btn-icon x-grid-page-next",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["next"])
27554         });
27555         this.last = this.addButton({
27556             tooltip: this.lastText,
27557             cls: "x-btn-icon x-grid-page-last",
27558             disabled: true,
27559             handler: this.onClick.createDelegate(this, ["last"])
27560         });
27561         //this.addSeparator();
27562         this.loading = this.addButton({
27563             tooltip: this.refreshText,
27564             cls: "x-btn-icon x-grid-loading",
27565             handler: this.onClick.createDelegate(this, ["refresh"])
27566         });
27567
27568         if(this.displayInfo){
27569             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27570         }
27571     },
27572
27573     // private
27574     updateInfo : function(){
27575         if(this.displayEl){
27576             var count = this.ds.getCount();
27577             var msg = count == 0 ?
27578                 this.emptyMsg :
27579                 String.format(
27580                     this.displayMsg,
27581                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27582                 );
27583             this.displayEl.update(msg);
27584         }
27585     },
27586
27587     // private
27588     onLoad : function(ds, r, o){
27589        this.cursor = o.params ? o.params.start : 0;
27590        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27591
27592        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27593        this.field.dom.value = ap;
27594        this.first.setDisabled(ap == 1);
27595        this.prev.setDisabled(ap == 1);
27596        this.next.setDisabled(ap == ps);
27597        this.last.setDisabled(ap == ps);
27598        this.loading.enable();
27599        this.updateInfo();
27600     },
27601
27602     // private
27603     getPageData : function(){
27604         var total = this.ds.getTotalCount();
27605         return {
27606             total : total,
27607             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27608             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27609         };
27610     },
27611
27612     // private
27613     onLoadError : function(){
27614         this.loading.enable();
27615     },
27616
27617     // private
27618     onPagingKeydown : function(e){
27619         var k = e.getKey();
27620         var d = this.getPageData();
27621         if(k == e.RETURN){
27622             var v = this.field.dom.value, pageNum;
27623             if(!v || isNaN(pageNum = parseInt(v, 10))){
27624                 this.field.dom.value = d.activePage;
27625                 return;
27626             }
27627             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27628             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27629             e.stopEvent();
27630         }
27631         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))
27632         {
27633           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27634           this.field.dom.value = pageNum;
27635           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27636           e.stopEvent();
27637         }
27638         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27639         {
27640           var v = this.field.dom.value, pageNum; 
27641           var increment = (e.shiftKey) ? 10 : 1;
27642           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27643             increment *= -1;
27644           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27645             this.field.dom.value = d.activePage;
27646             return;
27647           }
27648           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27649           {
27650             this.field.dom.value = parseInt(v, 10) + increment;
27651             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27652             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27653           }
27654           e.stopEvent();
27655         }
27656     },
27657
27658     // private
27659     beforeLoad : function(){
27660         if(this.loading){
27661             this.loading.disable();
27662         }
27663     },
27664
27665     // private
27666     onClick : function(which){
27667         var ds = this.ds;
27668         switch(which){
27669             case "first":
27670                 ds.load({params:{start: 0, limit: this.pageSize}});
27671             break;
27672             case "prev":
27673                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27674             break;
27675             case "next":
27676                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27677             break;
27678             case "last":
27679                 var total = ds.getTotalCount();
27680                 var extra = total % this.pageSize;
27681                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27682                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27683             break;
27684             case "refresh":
27685                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27686             break;
27687         }
27688     },
27689
27690     /**
27691      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27692      * @param {Roo.data.Store} store The data store to unbind
27693      */
27694     unbind : function(ds){
27695         ds.un("beforeload", this.beforeLoad, this);
27696         ds.un("load", this.onLoad, this);
27697         ds.un("loadexception", this.onLoadError, this);
27698         ds.un("remove", this.updateInfo, this);
27699         ds.un("add", this.updateInfo, this);
27700         this.ds = undefined;
27701     },
27702
27703     /**
27704      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27705      * @param {Roo.data.Store} store The data store to bind
27706      */
27707     bind : function(ds){
27708         ds.on("beforeload", this.beforeLoad, this);
27709         ds.on("load", this.onLoad, this);
27710         ds.on("loadexception", this.onLoadError, this);
27711         ds.on("remove", this.updateInfo, this);
27712         ds.on("add", this.updateInfo, this);
27713         this.ds = ds;
27714     }
27715 });/*
27716  * Based on:
27717  * Ext JS Library 1.1.1
27718  * Copyright(c) 2006-2007, Ext JS, LLC.
27719  *
27720  * Originally Released Under LGPL - original licence link has changed is not relivant.
27721  *
27722  * Fork - LGPL
27723  * <script type="text/javascript">
27724  */
27725
27726 /**
27727  * @class Roo.Resizable
27728  * @extends Roo.util.Observable
27729  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27730  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27731  * 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
27732  * the element will be wrapped for you automatically.</p>
27733  * <p>Here is the list of valid resize handles:</p>
27734  * <pre>
27735 Value   Description
27736 ------  -------------------
27737  'n'     north
27738  's'     south
27739  'e'     east
27740  'w'     west
27741  'nw'    northwest
27742  'sw'    southwest
27743  'se'    southeast
27744  'ne'    northeast
27745  'hd'    horizontal drag
27746  'all'   all
27747 </pre>
27748  * <p>Here's an example showing the creation of a typical Resizable:</p>
27749  * <pre><code>
27750 var resizer = new Roo.Resizable("element-id", {
27751     handles: 'all',
27752     minWidth: 200,
27753     minHeight: 100,
27754     maxWidth: 500,
27755     maxHeight: 400,
27756     pinned: true
27757 });
27758 resizer.on("resize", myHandler);
27759 </code></pre>
27760  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27761  * resizer.east.setDisplayed(false);</p>
27762  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27763  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27764  * resize operation's new size (defaults to [0, 0])
27765  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27766  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27767  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27768  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27769  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27770  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27771  * @cfg {Number} width The width of the element in pixels (defaults to null)
27772  * @cfg {Number} height The height of the element in pixels (defaults to null)
27773  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27774  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27775  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27776  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27777  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27778  * in favor of the handles config option (defaults to false)
27779  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27780  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27781  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27782  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27783  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27784  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27785  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27786  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27787  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27788  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27789  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27790  * @constructor
27791  * Create a new resizable component
27792  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27793  * @param {Object} config configuration options
27794   */
27795 Roo.Resizable = function(el, config)
27796 {
27797     this.el = Roo.get(el);
27798
27799     if(config && config.wrap){
27800         config.resizeChild = this.el;
27801         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27802         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27803         this.el.setStyle("overflow", "hidden");
27804         this.el.setPositioning(config.resizeChild.getPositioning());
27805         config.resizeChild.clearPositioning();
27806         if(!config.width || !config.height){
27807             var csize = config.resizeChild.getSize();
27808             this.el.setSize(csize.width, csize.height);
27809         }
27810         if(config.pinned && !config.adjustments){
27811             config.adjustments = "auto";
27812         }
27813     }
27814
27815     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27816     this.proxy.unselectable();
27817     this.proxy.enableDisplayMode('block');
27818
27819     Roo.apply(this, config);
27820
27821     if(this.pinned){
27822         this.disableTrackOver = true;
27823         this.el.addClass("x-resizable-pinned");
27824     }
27825     // if the element isn't positioned, make it relative
27826     var position = this.el.getStyle("position");
27827     if(position != "absolute" && position != "fixed"){
27828         this.el.setStyle("position", "relative");
27829     }
27830     if(!this.handles){ // no handles passed, must be legacy style
27831         this.handles = 's,e,se';
27832         if(this.multiDirectional){
27833             this.handles += ',n,w';
27834         }
27835     }
27836     if(this.handles == "all"){
27837         this.handles = "n s e w ne nw se sw";
27838     }
27839     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27840     var ps = Roo.Resizable.positions;
27841     for(var i = 0, len = hs.length; i < len; i++){
27842         if(hs[i] && ps[hs[i]]){
27843             var pos = ps[hs[i]];
27844             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27845         }
27846     }
27847     // legacy
27848     this.corner = this.southeast;
27849     
27850     // updateBox = the box can move..
27851     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27852         this.updateBox = true;
27853     }
27854
27855     this.activeHandle = null;
27856
27857     if(this.resizeChild){
27858         if(typeof this.resizeChild == "boolean"){
27859             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27860         }else{
27861             this.resizeChild = Roo.get(this.resizeChild, true);
27862         }
27863     }
27864     
27865     if(this.adjustments == "auto"){
27866         var rc = this.resizeChild;
27867         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27868         if(rc && (hw || hn)){
27869             rc.position("relative");
27870             rc.setLeft(hw ? hw.el.getWidth() : 0);
27871             rc.setTop(hn ? hn.el.getHeight() : 0);
27872         }
27873         this.adjustments = [
27874             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27875             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27876         ];
27877     }
27878
27879     if(this.draggable){
27880         this.dd = this.dynamic ?
27881             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27882         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27883     }
27884
27885     // public events
27886     this.addEvents({
27887         /**
27888          * @event beforeresize
27889          * Fired before resize is allowed. Set enabled to false to cancel resize.
27890          * @param {Roo.Resizable} this
27891          * @param {Roo.EventObject} e The mousedown event
27892          */
27893         "beforeresize" : true,
27894         /**
27895          * @event resize
27896          * Fired after a resize.
27897          * @param {Roo.Resizable} this
27898          * @param {Number} width The new width
27899          * @param {Number} height The new height
27900          * @param {Roo.EventObject} e The mouseup event
27901          */
27902         "resize" : true
27903     });
27904
27905     if(this.width !== null && this.height !== null){
27906         this.resizeTo(this.width, this.height);
27907     }else{
27908         this.updateChildSize();
27909     }
27910     if(Roo.isIE){
27911         this.el.dom.style.zoom = 1;
27912     }
27913     Roo.Resizable.superclass.constructor.call(this);
27914 };
27915
27916 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27917         resizeChild : false,
27918         adjustments : [0, 0],
27919         minWidth : 5,
27920         minHeight : 5,
27921         maxWidth : 10000,
27922         maxHeight : 10000,
27923         enabled : true,
27924         animate : false,
27925         duration : .35,
27926         dynamic : false,
27927         handles : false,
27928         multiDirectional : false,
27929         disableTrackOver : false,
27930         easing : 'easeOutStrong',
27931         widthIncrement : 0,
27932         heightIncrement : 0,
27933         pinned : false,
27934         width : null,
27935         height : null,
27936         preserveRatio : false,
27937         transparent: false,
27938         minX: 0,
27939         minY: 0,
27940         draggable: false,
27941
27942         /**
27943          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27944          */
27945         constrainTo: undefined,
27946         /**
27947          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27948          */
27949         resizeRegion: undefined,
27950
27951
27952     /**
27953      * Perform a manual resize
27954      * @param {Number} width
27955      * @param {Number} height
27956      */
27957     resizeTo : function(width, height){
27958         this.el.setSize(width, height);
27959         this.updateChildSize();
27960         this.fireEvent("resize", this, width, height, null);
27961     },
27962
27963     // private
27964     startSizing : function(e, handle){
27965         this.fireEvent("beforeresize", this, e);
27966         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27967
27968             if(!this.overlay){
27969                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27970                 this.overlay.unselectable();
27971                 this.overlay.enableDisplayMode("block");
27972                 this.overlay.on("mousemove", this.onMouseMove, this);
27973                 this.overlay.on("mouseup", this.onMouseUp, this);
27974             }
27975             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27976
27977             this.resizing = true;
27978             this.startBox = this.el.getBox();
27979             this.startPoint = e.getXY();
27980             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27981                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27982
27983             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27984             this.overlay.show();
27985
27986             if(this.constrainTo) {
27987                 var ct = Roo.get(this.constrainTo);
27988                 this.resizeRegion = ct.getRegion().adjust(
27989                     ct.getFrameWidth('t'),
27990                     ct.getFrameWidth('l'),
27991                     -ct.getFrameWidth('b'),
27992                     -ct.getFrameWidth('r')
27993                 );
27994             }
27995
27996             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27997             this.proxy.show();
27998             this.proxy.setBox(this.startBox);
27999             if(!this.dynamic){
28000                 this.proxy.setStyle('visibility', 'visible');
28001             }
28002         }
28003     },
28004
28005     // private
28006     onMouseDown : function(handle, e){
28007         if(this.enabled){
28008             e.stopEvent();
28009             this.activeHandle = handle;
28010             this.startSizing(e, handle);
28011         }
28012     },
28013
28014     // private
28015     onMouseUp : function(e){
28016         var size = this.resizeElement();
28017         this.resizing = false;
28018         this.handleOut();
28019         this.overlay.hide();
28020         this.proxy.hide();
28021         this.fireEvent("resize", this, size.width, size.height, e);
28022     },
28023
28024     // private
28025     updateChildSize : function(){
28026         if(this.resizeChild){
28027             var el = this.el;
28028             var child = this.resizeChild;
28029             var adj = this.adjustments;
28030             if(el.dom.offsetWidth){
28031                 var b = el.getSize(true);
28032                 child.setSize(b.width+adj[0], b.height+adj[1]);
28033             }
28034             // Second call here for IE
28035             // The first call enables instant resizing and
28036             // the second call corrects scroll bars if they
28037             // exist
28038             if(Roo.isIE){
28039                 setTimeout(function(){
28040                     if(el.dom.offsetWidth){
28041                         var b = el.getSize(true);
28042                         child.setSize(b.width+adj[0], b.height+adj[1]);
28043                     }
28044                 }, 10);
28045             }
28046         }
28047     },
28048
28049     // private
28050     snap : function(value, inc, min){
28051         if(!inc || !value) return value;
28052         var newValue = value;
28053         var m = value % inc;
28054         if(m > 0){
28055             if(m > (inc/2)){
28056                 newValue = value + (inc-m);
28057             }else{
28058                 newValue = value - m;
28059             }
28060         }
28061         return Math.max(min, newValue);
28062     },
28063
28064     // private
28065     resizeElement : function(){
28066         var box = this.proxy.getBox();
28067         if(this.updateBox){
28068             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28069         }else{
28070             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28071         }
28072         this.updateChildSize();
28073         if(!this.dynamic){
28074             this.proxy.hide();
28075         }
28076         return box;
28077     },
28078
28079     // private
28080     constrain : function(v, diff, m, mx){
28081         if(v - diff < m){
28082             diff = v - m;
28083         }else if(v - diff > mx){
28084             diff = mx - v;
28085         }
28086         return diff;
28087     },
28088
28089     // private
28090     onMouseMove : function(e){
28091         if(this.enabled){
28092             try{// try catch so if something goes wrong the user doesn't get hung
28093
28094             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28095                 return;
28096             }
28097
28098             //var curXY = this.startPoint;
28099             var curSize = this.curSize || this.startBox;
28100             var x = this.startBox.x, y = this.startBox.y;
28101             var ox = x, oy = y;
28102             var w = curSize.width, h = curSize.height;
28103             var ow = w, oh = h;
28104             var mw = this.minWidth, mh = this.minHeight;
28105             var mxw = this.maxWidth, mxh = this.maxHeight;
28106             var wi = this.widthIncrement;
28107             var hi = this.heightIncrement;
28108
28109             var eventXY = e.getXY();
28110             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28111             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28112
28113             var pos = this.activeHandle.position;
28114
28115             switch(pos){
28116                 case "east":
28117                     w += diffX;
28118                     w = Math.min(Math.max(mw, w), mxw);
28119                     break;
28120              
28121                 case "south":
28122                     h += diffY;
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "southeast":
28126                     w += diffX;
28127                     h += diffY;
28128                     w = Math.min(Math.max(mw, w), mxw);
28129                     h = Math.min(Math.max(mh, h), mxh);
28130                     break;
28131                 case "north":
28132                     diffY = this.constrain(h, diffY, mh, mxh);
28133                     y += diffY;
28134                     h -= diffY;
28135                     break;
28136                 case "hdrag":
28137                     
28138                     if (wi) {
28139                         var adiffX = Math.abs(diffX);
28140                         var sub = (adiffX % wi); // how much 
28141                         if (sub > (wi/2)) { // far enough to snap
28142                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28143                         } else {
28144                             // remove difference.. 
28145                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28146                         }
28147                     }
28148                     x += diffX;
28149                     x = Math.max(this.minX, x);
28150                     break;
28151                 case "west":
28152                     diffX = this.constrain(w, diffX, mw, mxw);
28153                     x += diffX;
28154                     w -= diffX;
28155                     break;
28156                 case "northeast":
28157                     w += diffX;
28158                     w = Math.min(Math.max(mw, w), mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     break;
28163                 case "northwest":
28164                     diffX = this.constrain(w, diffX, mw, mxw);
28165                     diffY = this.constrain(h, diffY, mh, mxh);
28166                     y += diffY;
28167                     h -= diffY;
28168                     x += diffX;
28169                     w -= diffX;
28170                     break;
28171                case "southwest":
28172                     diffX = this.constrain(w, diffX, mw, mxw);
28173                     h += diffY;
28174                     h = Math.min(Math.max(mh, h), mxh);
28175                     x += diffX;
28176                     w -= diffX;
28177                     break;
28178             }
28179
28180             var sw = this.snap(w, wi, mw);
28181             var sh = this.snap(h, hi, mh);
28182             if(sw != w || sh != h){
28183                 switch(pos){
28184                     case "northeast":
28185                         y -= sh - h;
28186                     break;
28187                     case "north":
28188                         y -= sh - h;
28189                         break;
28190                     case "southwest":
28191                         x -= sw - w;
28192                     break;
28193                     case "west":
28194                         x -= sw - w;
28195                         break;
28196                     case "northwest":
28197                         x -= sw - w;
28198                         y -= sh - h;
28199                     break;
28200                 }
28201                 w = sw;
28202                 h = sh;
28203             }
28204
28205             if(this.preserveRatio){
28206                 switch(pos){
28207                     case "southeast":
28208                     case "east":
28209                         h = oh * (w/ow);
28210                         h = Math.min(Math.max(mh, h), mxh);
28211                         w = ow * (h/oh);
28212                        break;
28213                     case "south":
28214                         w = ow * (h/oh);
28215                         w = Math.min(Math.max(mw, w), mxw);
28216                         h = oh * (w/ow);
28217                         break;
28218                     case "northeast":
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                     break;
28223                     case "north":
28224                         var tw = w;
28225                         w = ow * (h/oh);
28226                         w = Math.min(Math.max(mw, w), mxw);
28227                         h = oh * (w/ow);
28228                         x += (tw - w) / 2;
28229                         break;
28230                     case "southwest":
28231                         h = oh * (w/ow);
28232                         h = Math.min(Math.max(mh, h), mxh);
28233                         var tw = w;
28234                         w = ow * (h/oh);
28235                         x += tw - w;
28236                         break;
28237                     case "west":
28238                         var th = h;
28239                         h = oh * (w/ow);
28240                         h = Math.min(Math.max(mh, h), mxh);
28241                         y += (th - h) / 2;
28242                         var tw = w;
28243                         w = ow * (h/oh);
28244                         x += tw - w;
28245                        break;
28246                     case "northwest":
28247                         var tw = w;
28248                         var th = h;
28249                         h = oh * (w/ow);
28250                         h = Math.min(Math.max(mh, h), mxh);
28251                         w = ow * (h/oh);
28252                         y += th - h;
28253                         x += tw - w;
28254                        break;
28255
28256                 }
28257             }
28258             if (pos == 'hdrag') {
28259                 w = ow;
28260             }
28261             this.proxy.setBounds(x, y, w, h);
28262             if(this.dynamic){
28263                 this.resizeElement();
28264             }
28265             }catch(e){}
28266         }
28267     },
28268
28269     // private
28270     handleOver : function(){
28271         if(this.enabled){
28272             this.el.addClass("x-resizable-over");
28273         }
28274     },
28275
28276     // private
28277     handleOut : function(){
28278         if(!this.resizing){
28279             this.el.removeClass("x-resizable-over");
28280         }
28281     },
28282
28283     /**
28284      * Returns the element this component is bound to.
28285      * @return {Roo.Element}
28286      */
28287     getEl : function(){
28288         return this.el;
28289     },
28290
28291     /**
28292      * Returns the resizeChild element (or null).
28293      * @return {Roo.Element}
28294      */
28295     getResizeChild : function(){
28296         return this.resizeChild;
28297     },
28298
28299     /**
28300      * Destroys this resizable. If the element was wrapped and
28301      * removeEl is not true then the element remains.
28302      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28303      */
28304     destroy : function(removeEl){
28305         this.proxy.remove();
28306         if(this.overlay){
28307             this.overlay.removeAllListeners();
28308             this.overlay.remove();
28309         }
28310         var ps = Roo.Resizable.positions;
28311         for(var k in ps){
28312             if(typeof ps[k] != "function" && this[ps[k]]){
28313                 var h = this[ps[k]];
28314                 h.el.removeAllListeners();
28315                 h.el.remove();
28316             }
28317         }
28318         if(removeEl){
28319             this.el.update("");
28320             this.el.remove();
28321         }
28322     }
28323 });
28324
28325 // private
28326 // hash to map config positions to true positions
28327 Roo.Resizable.positions = {
28328     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28329     hd: "hdrag"
28330 };
28331
28332 // private
28333 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28334     if(!this.tpl){
28335         // only initialize the template if resizable is used
28336         var tpl = Roo.DomHelper.createTemplate(
28337             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28338         );
28339         tpl.compile();
28340         Roo.Resizable.Handle.prototype.tpl = tpl;
28341     }
28342     this.position = pos;
28343     this.rz = rz;
28344     // show north drag fro topdra
28345     var handlepos = pos == 'hdrag' ? 'north' : pos;
28346     
28347     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28348     if (pos == 'hdrag') {
28349         this.el.setStyle('cursor', 'pointer');
28350     }
28351     this.el.unselectable();
28352     if(transparent){
28353         this.el.setOpacity(0);
28354     }
28355     this.el.on("mousedown", this.onMouseDown, this);
28356     if(!disableTrackOver){
28357         this.el.on("mouseover", this.onMouseOver, this);
28358         this.el.on("mouseout", this.onMouseOut, this);
28359     }
28360 };
28361
28362 // private
28363 Roo.Resizable.Handle.prototype = {
28364     afterResize : function(rz){
28365         // do nothing
28366     },
28367     // private
28368     onMouseDown : function(e){
28369         this.rz.onMouseDown(this, e);
28370     },
28371     // private
28372     onMouseOver : function(e){
28373         this.rz.handleOver(this, e);
28374     },
28375     // private
28376     onMouseOut : function(e){
28377         this.rz.handleOut(this, e);
28378     }
28379 };/*
28380  * Based on:
28381  * Ext JS Library 1.1.1
28382  * Copyright(c) 2006-2007, Ext JS, LLC.
28383  *
28384  * Originally Released Under LGPL - original licence link has changed is not relivant.
28385  *
28386  * Fork - LGPL
28387  * <script type="text/javascript">
28388  */
28389
28390 /**
28391  * @class Roo.Editor
28392  * @extends Roo.Component
28393  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28394  * @constructor
28395  * Create a new Editor
28396  * @param {Roo.form.Field} field The Field object (or descendant)
28397  * @param {Object} config The config object
28398  */
28399 Roo.Editor = function(field, config){
28400     Roo.Editor.superclass.constructor.call(this, config);
28401     this.field = field;
28402     this.addEvents({
28403         /**
28404              * @event beforestartedit
28405              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28406              * false from the handler of this event.
28407              * @param {Editor} this
28408              * @param {Roo.Element} boundEl The underlying element bound to this editor
28409              * @param {Mixed} value The field value being set
28410              */
28411         "beforestartedit" : true,
28412         /**
28413              * @event startedit
28414              * Fires when this editor is displayed
28415              * @param {Roo.Element} boundEl The underlying element bound to this editor
28416              * @param {Mixed} value The starting field value
28417              */
28418         "startedit" : true,
28419         /**
28420              * @event beforecomplete
28421              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28422              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28423              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28424              * event will not fire since no edit actually occurred.
28425              * @param {Editor} this
28426              * @param {Mixed} value The current field value
28427              * @param {Mixed} startValue The original field value
28428              */
28429         "beforecomplete" : true,
28430         /**
28431              * @event complete
28432              * Fires after editing is complete and any changed value has been written to the underlying field.
28433              * @param {Editor} this
28434              * @param {Mixed} value The current field value
28435              * @param {Mixed} startValue The original field value
28436              */
28437         "complete" : true,
28438         /**
28439          * @event specialkey
28440          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28441          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28442          * @param {Roo.form.Field} this
28443          * @param {Roo.EventObject} e The event object
28444          */
28445         "specialkey" : true
28446     });
28447 };
28448
28449 Roo.extend(Roo.Editor, Roo.Component, {
28450     /**
28451      * @cfg {Boolean/String} autosize
28452      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28453      * or "height" to adopt the height only (defaults to false)
28454      */
28455     /**
28456      * @cfg {Boolean} revertInvalid
28457      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28458      * validation fails (defaults to true)
28459      */
28460     /**
28461      * @cfg {Boolean} ignoreNoChange
28462      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28463      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28464      * will never be ignored.
28465      */
28466     /**
28467      * @cfg {Boolean} hideEl
28468      * False to keep the bound element visible while the editor is displayed (defaults to true)
28469      */
28470     /**
28471      * @cfg {Mixed} value
28472      * The data value of the underlying field (defaults to "")
28473      */
28474     value : "",
28475     /**
28476      * @cfg {String} alignment
28477      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28478      */
28479     alignment: "c-c?",
28480     /**
28481      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28482      * for bottom-right shadow (defaults to "frame")
28483      */
28484     shadow : "frame",
28485     /**
28486      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28487      */
28488     constrain : false,
28489     /**
28490      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28491      */
28492     completeOnEnter : false,
28493     /**
28494      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28495      */
28496     cancelOnEsc : false,
28497     /**
28498      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28499      */
28500     updateEl : false,
28501
28502     // private
28503     onRender : function(ct, position){
28504         this.el = new Roo.Layer({
28505             shadow: this.shadow,
28506             cls: "x-editor",
28507             parentEl : ct,
28508             shim : this.shim,
28509             shadowOffset:4,
28510             id: this.id,
28511             constrain: this.constrain
28512         });
28513         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28514         if(this.field.msgTarget != 'title'){
28515             this.field.msgTarget = 'qtip';
28516         }
28517         this.field.render(this.el);
28518         if(Roo.isGecko){
28519             this.field.el.dom.setAttribute('autocomplete', 'off');
28520         }
28521         this.field.on("specialkey", this.onSpecialKey, this);
28522         if(this.swallowKeys){
28523             this.field.el.swallowEvent(['keydown','keypress']);
28524         }
28525         this.field.show();
28526         this.field.on("blur", this.onBlur, this);
28527         if(this.field.grow){
28528             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28529         }
28530     },
28531
28532     onSpecialKey : function(field, e)
28533     {
28534         //Roo.log('editor onSpecialKey');
28535         if(this.completeOnEnter && e.getKey() == e.ENTER){
28536             e.stopEvent();
28537             this.completeEdit();
28538             return;
28539         }
28540         // do not fire special key otherwise it might hide close the editor...
28541         if(e.getKey() == e.ENTER){    
28542             return;
28543         }
28544         if(this.cancelOnEsc && e.getKey() == e.ESC){
28545             this.cancelEdit();
28546             return;
28547         } 
28548         this.fireEvent('specialkey', field, e);
28549     
28550     },
28551
28552     /**
28553      * Starts the editing process and shows the editor.
28554      * @param {String/HTMLElement/Element} el The element to edit
28555      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28556       * to the innerHTML of el.
28557      */
28558     startEdit : function(el, value){
28559         if(this.editing){
28560             this.completeEdit();
28561         }
28562         this.boundEl = Roo.get(el);
28563         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28564         if(!this.rendered){
28565             this.render(this.parentEl || document.body);
28566         }
28567         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28568             return;
28569         }
28570         this.startValue = v;
28571         this.field.setValue(v);
28572         if(this.autoSize){
28573             var sz = this.boundEl.getSize();
28574             switch(this.autoSize){
28575                 case "width":
28576                 this.setSize(sz.width,  "");
28577                 break;
28578                 case "height":
28579                 this.setSize("",  sz.height);
28580                 break;
28581                 default:
28582                 this.setSize(sz.width,  sz.height);
28583             }
28584         }
28585         this.el.alignTo(this.boundEl, this.alignment);
28586         this.editing = true;
28587         if(Roo.QuickTips){
28588             Roo.QuickTips.disable();
28589         }
28590         this.show();
28591     },
28592
28593     /**
28594      * Sets the height and width of this editor.
28595      * @param {Number} width The new width
28596      * @param {Number} height The new height
28597      */
28598     setSize : function(w, h){
28599         this.field.setSize(w, h);
28600         if(this.el){
28601             this.el.sync();
28602         }
28603     },
28604
28605     /**
28606      * Realigns the editor to the bound field based on the current alignment config value.
28607      */
28608     realign : function(){
28609         this.el.alignTo(this.boundEl, this.alignment);
28610     },
28611
28612     /**
28613      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28614      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28615      */
28616     completeEdit : function(remainVisible){
28617         if(!this.editing){
28618             return;
28619         }
28620         var v = this.getValue();
28621         if(this.revertInvalid !== false && !this.field.isValid()){
28622             v = this.startValue;
28623             this.cancelEdit(true);
28624         }
28625         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28626             this.editing = false;
28627             this.hide();
28628             return;
28629         }
28630         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28631             this.editing = false;
28632             if(this.updateEl && this.boundEl){
28633                 this.boundEl.update(v);
28634             }
28635             if(remainVisible !== true){
28636                 this.hide();
28637             }
28638             this.fireEvent("complete", this, v, this.startValue);
28639         }
28640     },
28641
28642     // private
28643     onShow : function(){
28644         this.el.show();
28645         if(this.hideEl !== false){
28646             this.boundEl.hide();
28647         }
28648         this.field.show();
28649         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28650             this.fixIEFocus = true;
28651             this.deferredFocus.defer(50, this);
28652         }else{
28653             this.field.focus();
28654         }
28655         this.fireEvent("startedit", this.boundEl, this.startValue);
28656     },
28657
28658     deferredFocus : function(){
28659         if(this.editing){
28660             this.field.focus();
28661         }
28662     },
28663
28664     /**
28665      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28666      * reverted to the original starting value.
28667      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28668      * cancel (defaults to false)
28669      */
28670     cancelEdit : function(remainVisible){
28671         if(this.editing){
28672             this.setValue(this.startValue);
28673             if(remainVisible !== true){
28674                 this.hide();
28675             }
28676         }
28677     },
28678
28679     // private
28680     onBlur : function(){
28681         if(this.allowBlur !== true && this.editing){
28682             this.completeEdit();
28683         }
28684     },
28685
28686     // private
28687     onHide : function(){
28688         if(this.editing){
28689             this.completeEdit();
28690             return;
28691         }
28692         this.field.blur();
28693         if(this.field.collapse){
28694             this.field.collapse();
28695         }
28696         this.el.hide();
28697         if(this.hideEl !== false){
28698             this.boundEl.show();
28699         }
28700         if(Roo.QuickTips){
28701             Roo.QuickTips.enable();
28702         }
28703     },
28704
28705     /**
28706      * Sets the data value of the editor
28707      * @param {Mixed} value Any valid value supported by the underlying field
28708      */
28709     setValue : function(v){
28710         this.field.setValue(v);
28711     },
28712
28713     /**
28714      * Gets the data value of the editor
28715      * @return {Mixed} The data value
28716      */
28717     getValue : function(){
28718         return this.field.getValue();
28719     }
28720 });/*
28721  * Based on:
28722  * Ext JS Library 1.1.1
28723  * Copyright(c) 2006-2007, Ext JS, LLC.
28724  *
28725  * Originally Released Under LGPL - original licence link has changed is not relivant.
28726  *
28727  * Fork - LGPL
28728  * <script type="text/javascript">
28729  */
28730  
28731 /**
28732  * @class Roo.BasicDialog
28733  * @extends Roo.util.Observable
28734  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28735  * <pre><code>
28736 var dlg = new Roo.BasicDialog("my-dlg", {
28737     height: 200,
28738     width: 300,
28739     minHeight: 100,
28740     minWidth: 150,
28741     modal: true,
28742     proxyDrag: true,
28743     shadow: true
28744 });
28745 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28746 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28747 dlg.addButton('Cancel', dlg.hide, dlg);
28748 dlg.show();
28749 </code></pre>
28750   <b>A Dialog should always be a direct child of the body element.</b>
28751  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28752  * @cfg {String} title Default text to display in the title bar (defaults to null)
28753  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28754  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28755  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28756  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28757  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28758  * (defaults to null with no animation)
28759  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28760  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28761  * property for valid values (defaults to 'all')
28762  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28763  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28764  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28765  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28766  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28767  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28768  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28769  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28770  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28771  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28772  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28773  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28774  * draggable = true (defaults to false)
28775  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28776  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28777  * shadow (defaults to false)
28778  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28779  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28780  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28781  * @cfg {Array} buttons Array of buttons
28782  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28783  * @constructor
28784  * Create a new BasicDialog.
28785  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28786  * @param {Object} config Configuration options
28787  */
28788 Roo.BasicDialog = function(el, config){
28789     this.el = Roo.get(el);
28790     var dh = Roo.DomHelper;
28791     if(!this.el && config && config.autoCreate){
28792         if(typeof config.autoCreate == "object"){
28793             if(!config.autoCreate.id){
28794                 config.autoCreate.id = el;
28795             }
28796             this.el = dh.append(document.body,
28797                         config.autoCreate, true);
28798         }else{
28799             this.el = dh.append(document.body,
28800                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28801         }
28802     }
28803     el = this.el;
28804     el.setDisplayed(true);
28805     el.hide = this.hideAction;
28806     this.id = el.id;
28807     el.addClass("x-dlg");
28808
28809     Roo.apply(this, config);
28810
28811     this.proxy = el.createProxy("x-dlg-proxy");
28812     this.proxy.hide = this.hideAction;
28813     this.proxy.setOpacity(.5);
28814     this.proxy.hide();
28815
28816     if(config.width){
28817         el.setWidth(config.width);
28818     }
28819     if(config.height){
28820         el.setHeight(config.height);
28821     }
28822     this.size = el.getSize();
28823     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28824         this.xy = [config.x,config.y];
28825     }else{
28826         this.xy = el.getCenterXY(true);
28827     }
28828     /** The header element @type Roo.Element */
28829     this.header = el.child("> .x-dlg-hd");
28830     /** The body element @type Roo.Element */
28831     this.body = el.child("> .x-dlg-bd");
28832     /** The footer element @type Roo.Element */
28833     this.footer = el.child("> .x-dlg-ft");
28834
28835     if(!this.header){
28836         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28837     }
28838     if(!this.body){
28839         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28840     }
28841
28842     this.header.unselectable();
28843     if(this.title){
28844         this.header.update(this.title);
28845     }
28846     // this element allows the dialog to be focused for keyboard event
28847     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28848     this.focusEl.swallowEvent("click", true);
28849
28850     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28851
28852     // wrap the body and footer for special rendering
28853     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28854     if(this.footer){
28855         this.bwrap.dom.appendChild(this.footer.dom);
28856     }
28857
28858     this.bg = this.el.createChild({
28859         tag: "div", cls:"x-dlg-bg",
28860         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28861     });
28862     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28863
28864
28865     if(this.autoScroll !== false && !this.autoTabs){
28866         this.body.setStyle("overflow", "auto");
28867     }
28868
28869     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28870
28871     if(this.closable !== false){
28872         this.el.addClass("x-dlg-closable");
28873         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28874         this.close.on("click", this.closeClick, this);
28875         this.close.addClassOnOver("x-dlg-close-over");
28876     }
28877     if(this.collapsible !== false){
28878         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28879         this.collapseBtn.on("click", this.collapseClick, this);
28880         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28881         this.header.on("dblclick", this.collapseClick, this);
28882     }
28883     if(this.resizable !== false){
28884         this.el.addClass("x-dlg-resizable");
28885         this.resizer = new Roo.Resizable(el, {
28886             minWidth: this.minWidth || 80,
28887             minHeight:this.minHeight || 80,
28888             handles: this.resizeHandles || "all",
28889             pinned: true
28890         });
28891         this.resizer.on("beforeresize", this.beforeResize, this);
28892         this.resizer.on("resize", this.onResize, this);
28893     }
28894     if(this.draggable !== false){
28895         el.addClass("x-dlg-draggable");
28896         if (!this.proxyDrag) {
28897             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28898         }
28899         else {
28900             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28901         }
28902         dd.setHandleElId(this.header.id);
28903         dd.endDrag = this.endMove.createDelegate(this);
28904         dd.startDrag = this.startMove.createDelegate(this);
28905         dd.onDrag = this.onDrag.createDelegate(this);
28906         dd.scroll = false;
28907         this.dd = dd;
28908     }
28909     if(this.modal){
28910         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28911         this.mask.enableDisplayMode("block");
28912         this.mask.hide();
28913         this.el.addClass("x-dlg-modal");
28914     }
28915     if(this.shadow){
28916         this.shadow = new Roo.Shadow({
28917             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28918             offset : this.shadowOffset
28919         });
28920     }else{
28921         this.shadowOffset = 0;
28922     }
28923     if(Roo.useShims && this.shim !== false){
28924         this.shim = this.el.createShim();
28925         this.shim.hide = this.hideAction;
28926         this.shim.hide();
28927     }else{
28928         this.shim = false;
28929     }
28930     if(this.autoTabs){
28931         this.initTabs();
28932     }
28933     if (this.buttons) { 
28934         var bts= this.buttons;
28935         this.buttons = [];
28936         Roo.each(bts, function(b) {
28937             this.addButton(b);
28938         }, this);
28939     }
28940     
28941     
28942     this.addEvents({
28943         /**
28944          * @event keydown
28945          * Fires when a key is pressed
28946          * @param {Roo.BasicDialog} this
28947          * @param {Roo.EventObject} e
28948          */
28949         "keydown" : true,
28950         /**
28951          * @event move
28952          * Fires when this dialog is moved by the user.
28953          * @param {Roo.BasicDialog} this
28954          * @param {Number} x The new page X
28955          * @param {Number} y The new page Y
28956          */
28957         "move" : true,
28958         /**
28959          * @event resize
28960          * Fires when this dialog is resized by the user.
28961          * @param {Roo.BasicDialog} this
28962          * @param {Number} width The new width
28963          * @param {Number} height The new height
28964          */
28965         "resize" : true,
28966         /**
28967          * @event beforehide
28968          * Fires before this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "beforehide" : true,
28972         /**
28973          * @event hide
28974          * Fires when this dialog is hidden.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "hide" : true,
28978         /**
28979          * @event beforeshow
28980          * Fires before this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "beforeshow" : true,
28984         /**
28985          * @event show
28986          * Fires when this dialog is shown.
28987          * @param {Roo.BasicDialog} this
28988          */
28989         "show" : true
28990     });
28991     el.on("keydown", this.onKeyDown, this);
28992     el.on("mousedown", this.toFront, this);
28993     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28994     this.el.hide();
28995     Roo.DialogManager.register(this);
28996     Roo.BasicDialog.superclass.constructor.call(this);
28997 };
28998
28999 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29000     shadowOffset: Roo.isIE ? 6 : 5,
29001     minHeight: 80,
29002     minWidth: 200,
29003     minButtonWidth: 75,
29004     defaultButton: null,
29005     buttonAlign: "right",
29006     tabTag: 'div',
29007     firstShow: true,
29008
29009     /**
29010      * Sets the dialog title text
29011      * @param {String} text The title text to display
29012      * @return {Roo.BasicDialog} this
29013      */
29014     setTitle : function(text){
29015         this.header.update(text);
29016         return this;
29017     },
29018
29019     // private
29020     closeClick : function(){
29021         this.hide();
29022     },
29023
29024     // private
29025     collapseClick : function(){
29026         this[this.collapsed ? "expand" : "collapse"]();
29027     },
29028
29029     /**
29030      * Collapses the dialog to its minimized state (only the title bar is visible).
29031      * Equivalent to the user clicking the collapse dialog button.
29032      */
29033     collapse : function(){
29034         if(!this.collapsed){
29035             this.collapsed = true;
29036             this.el.addClass("x-dlg-collapsed");
29037             this.restoreHeight = this.el.getHeight();
29038             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29039         }
29040     },
29041
29042     /**
29043      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29044      * clicking the expand dialog button.
29045      */
29046     expand : function(){
29047         if(this.collapsed){
29048             this.collapsed = false;
29049             this.el.removeClass("x-dlg-collapsed");
29050             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29051         }
29052     },
29053
29054     /**
29055      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29056      * @return {Roo.TabPanel} The tabs component
29057      */
29058     initTabs : function(){
29059         var tabs = this.getTabs();
29060         while(tabs.getTab(0)){
29061             tabs.removeTab(0);
29062         }
29063         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29064             var dom = el.dom;
29065             tabs.addTab(Roo.id(dom), dom.title);
29066             dom.title = "";
29067         });
29068         tabs.activate(0);
29069         return tabs;
29070     },
29071
29072     // private
29073     beforeResize : function(){
29074         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29075     },
29076
29077     // private
29078     onResize : function(){
29079         this.refreshSize();
29080         this.syncBodyHeight();
29081         this.adjustAssets();
29082         this.focus();
29083         this.fireEvent("resize", this, this.size.width, this.size.height);
29084     },
29085
29086     // private
29087     onKeyDown : function(e){
29088         if(this.isVisible()){
29089             this.fireEvent("keydown", this, e);
29090         }
29091     },
29092
29093     /**
29094      * Resizes the dialog.
29095      * @param {Number} width
29096      * @param {Number} height
29097      * @return {Roo.BasicDialog} this
29098      */
29099     resizeTo : function(width, height){
29100         this.el.setSize(width, height);
29101         this.size = {width: width, height: height};
29102         this.syncBodyHeight();
29103         if(this.fixedcenter){
29104             this.center();
29105         }
29106         if(this.isVisible()){
29107             this.constrainXY();
29108             this.adjustAssets();
29109         }
29110         this.fireEvent("resize", this, width, height);
29111         return this;
29112     },
29113
29114
29115     /**
29116      * Resizes the dialog to fit the specified content size.
29117      * @param {Number} width
29118      * @param {Number} height
29119      * @return {Roo.BasicDialog} this
29120      */
29121     setContentSize : function(w, h){
29122         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29123         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29124         //if(!this.el.isBorderBox()){
29125             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29126             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29127         //}
29128         if(this.tabs){
29129             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29130             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29131         }
29132         this.resizeTo(w, h);
29133         return this;
29134     },
29135
29136     /**
29137      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29138      * executed in response to a particular key being pressed while the dialog is active.
29139      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29140      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29141      * @param {Function} fn The function to call
29142      * @param {Object} scope (optional) The scope of the function
29143      * @return {Roo.BasicDialog} this
29144      */
29145     addKeyListener : function(key, fn, scope){
29146         var keyCode, shift, ctrl, alt;
29147         if(typeof key == "object" && !(key instanceof Array)){
29148             keyCode = key["key"];
29149             shift = key["shift"];
29150             ctrl = key["ctrl"];
29151             alt = key["alt"];
29152         }else{
29153             keyCode = key;
29154         }
29155         var handler = function(dlg, e){
29156             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29157                 var k = e.getKey();
29158                 if(keyCode instanceof Array){
29159                     for(var i = 0, len = keyCode.length; i < len; i++){
29160                         if(keyCode[i] == k){
29161                           fn.call(scope || window, dlg, k, e);
29162                           return;
29163                         }
29164                     }
29165                 }else{
29166                     if(k == keyCode){
29167                         fn.call(scope || window, dlg, k, e);
29168                     }
29169                 }
29170             }
29171         };
29172         this.on("keydown", handler);
29173         return this;
29174     },
29175
29176     /**
29177      * Returns the TabPanel component (creates it if it doesn't exist).
29178      * Note: If you wish to simply check for the existence of tabs without creating them,
29179      * check for a null 'tabs' property.
29180      * @return {Roo.TabPanel} The tabs component
29181      */
29182     getTabs : function(){
29183         if(!this.tabs){
29184             this.el.addClass("x-dlg-auto-tabs");
29185             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29186             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29187         }
29188         return this.tabs;
29189     },
29190
29191     /**
29192      * Adds a button to the footer section of the dialog.
29193      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29194      * object or a valid Roo.DomHelper element config
29195      * @param {Function} handler The function called when the button is clicked
29196      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29197      * @return {Roo.Button} The new button
29198      */
29199     addButton : function(config, handler, scope){
29200         var dh = Roo.DomHelper;
29201         if(!this.footer){
29202             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29203         }
29204         if(!this.btnContainer){
29205             var tb = this.footer.createChild({
29206
29207                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29208                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29209             }, null, true);
29210             this.btnContainer = tb.firstChild.firstChild.firstChild;
29211         }
29212         var bconfig = {
29213             handler: handler,
29214             scope: scope,
29215             minWidth: this.minButtonWidth,
29216             hideParent:true
29217         };
29218         if(typeof config == "string"){
29219             bconfig.text = config;
29220         }else{
29221             if(config.tag){
29222                 bconfig.dhconfig = config;
29223             }else{
29224                 Roo.apply(bconfig, config);
29225             }
29226         }
29227         var fc = false;
29228         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29229             bconfig.position = Math.max(0, bconfig.position);
29230             fc = this.btnContainer.childNodes[bconfig.position];
29231         }
29232          
29233         var btn = new Roo.Button(
29234             fc ? 
29235                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29236                 : this.btnContainer.appendChild(document.createElement("td")),
29237             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29238             bconfig
29239         );
29240         this.syncBodyHeight();
29241         if(!this.buttons){
29242             /**
29243              * Array of all the buttons that have been added to this dialog via addButton
29244              * @type Array
29245              */
29246             this.buttons = [];
29247         }
29248         this.buttons.push(btn);
29249         return btn;
29250     },
29251
29252     /**
29253      * Sets the default button to be focused when the dialog is displayed.
29254      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29255      * @return {Roo.BasicDialog} this
29256      */
29257     setDefaultButton : function(btn){
29258         this.defaultButton = btn;
29259         return this;
29260     },
29261
29262     // private
29263     getHeaderFooterHeight : function(safe){
29264         var height = 0;
29265         if(this.header){
29266            height += this.header.getHeight();
29267         }
29268         if(this.footer){
29269            var fm = this.footer.getMargins();
29270             height += (this.footer.getHeight()+fm.top+fm.bottom);
29271         }
29272         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29273         height += this.centerBg.getPadding("tb");
29274         return height;
29275     },
29276
29277     // private
29278     syncBodyHeight : function(){
29279         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29280         var height = this.size.height - this.getHeaderFooterHeight(false);
29281         bd.setHeight(height-bd.getMargins("tb"));
29282         var hh = this.header.getHeight();
29283         var h = this.size.height-hh;
29284         cb.setHeight(h);
29285         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29286         bw.setHeight(h-cb.getPadding("tb"));
29287         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29288         bd.setWidth(bw.getWidth(true));
29289         if(this.tabs){
29290             this.tabs.syncHeight();
29291             if(Roo.isIE){
29292                 this.tabs.el.repaint();
29293             }
29294         }
29295     },
29296
29297     /**
29298      * Restores the previous state of the dialog if Roo.state is configured.
29299      * @return {Roo.BasicDialog} this
29300      */
29301     restoreState : function(){
29302         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29303         if(box && box.width){
29304             this.xy = [box.x, box.y];
29305             this.resizeTo(box.width, box.height);
29306         }
29307         return this;
29308     },
29309
29310     // private
29311     beforeShow : function(){
29312         this.expand();
29313         if(this.fixedcenter){
29314             this.xy = this.el.getCenterXY(true);
29315         }
29316         if(this.modal){
29317             Roo.get(document.body).addClass("x-body-masked");
29318             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29319             this.mask.show();
29320         }
29321         this.constrainXY();
29322     },
29323
29324     // private
29325     animShow : function(){
29326         var b = Roo.get(this.animateTarget).getBox();
29327         this.proxy.setSize(b.width, b.height);
29328         this.proxy.setLocation(b.x, b.y);
29329         this.proxy.show();
29330         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29331                     true, .35, this.showEl.createDelegate(this));
29332     },
29333
29334     /**
29335      * Shows the dialog.
29336      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29337      * @return {Roo.BasicDialog} this
29338      */
29339     show : function(animateTarget){
29340         if (this.fireEvent("beforeshow", this) === false){
29341             return;
29342         }
29343         if(this.syncHeightBeforeShow){
29344             this.syncBodyHeight();
29345         }else if(this.firstShow){
29346             this.firstShow = false;
29347             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29348         }
29349         this.animateTarget = animateTarget || this.animateTarget;
29350         if(!this.el.isVisible()){
29351             this.beforeShow();
29352             if(this.animateTarget && Roo.get(this.animateTarget)){
29353                 this.animShow();
29354             }else{
29355                 this.showEl();
29356             }
29357         }
29358         return this;
29359     },
29360
29361     // private
29362     showEl : function(){
29363         this.proxy.hide();
29364         this.el.setXY(this.xy);
29365         this.el.show();
29366         this.adjustAssets(true);
29367         this.toFront();
29368         this.focus();
29369         // IE peekaboo bug - fix found by Dave Fenwick
29370         if(Roo.isIE){
29371             this.el.repaint();
29372         }
29373         this.fireEvent("show", this);
29374     },
29375
29376     /**
29377      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29378      * dialog itself will receive focus.
29379      */
29380     focus : function(){
29381         if(this.defaultButton){
29382             this.defaultButton.focus();
29383         }else{
29384             this.focusEl.focus();
29385         }
29386     },
29387
29388     // private
29389     constrainXY : function(){
29390         if(this.constraintoviewport !== false){
29391             if(!this.viewSize){
29392                 if(this.container){
29393                     var s = this.container.getSize();
29394                     this.viewSize = [s.width, s.height];
29395                 }else{
29396                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29397                 }
29398             }
29399             var s = Roo.get(this.container||document).getScroll();
29400
29401             var x = this.xy[0], y = this.xy[1];
29402             var w = this.size.width, h = this.size.height;
29403             var vw = this.viewSize[0], vh = this.viewSize[1];
29404             // only move it if it needs it
29405             var moved = false;
29406             // first validate right/bottom
29407             if(x + w > vw+s.left){
29408                 x = vw - w;
29409                 moved = true;
29410             }
29411             if(y + h > vh+s.top){
29412                 y = vh - h;
29413                 moved = true;
29414             }
29415             // then make sure top/left isn't negative
29416             if(x < s.left){
29417                 x = s.left;
29418                 moved = true;
29419             }
29420             if(y < s.top){
29421                 y = s.top;
29422                 moved = true;
29423             }
29424             if(moved){
29425                 // cache xy
29426                 this.xy = [x, y];
29427                 if(this.isVisible()){
29428                     this.el.setLocation(x, y);
29429                     this.adjustAssets();
29430                 }
29431             }
29432         }
29433     },
29434
29435     // private
29436     onDrag : function(){
29437         if(!this.proxyDrag){
29438             this.xy = this.el.getXY();
29439             this.adjustAssets();
29440         }
29441     },
29442
29443     // private
29444     adjustAssets : function(doShow){
29445         var x = this.xy[0], y = this.xy[1];
29446         var w = this.size.width, h = this.size.height;
29447         if(doShow === true){
29448             if(this.shadow){
29449                 this.shadow.show(this.el);
29450             }
29451             if(this.shim){
29452                 this.shim.show();
29453             }
29454         }
29455         if(this.shadow && this.shadow.isVisible()){
29456             this.shadow.show(this.el);
29457         }
29458         if(this.shim && this.shim.isVisible()){
29459             this.shim.setBounds(x, y, w, h);
29460         }
29461     },
29462
29463     // private
29464     adjustViewport : function(w, h){
29465         if(!w || !h){
29466             w = Roo.lib.Dom.getViewWidth();
29467             h = Roo.lib.Dom.getViewHeight();
29468         }
29469         // cache the size
29470         this.viewSize = [w, h];
29471         if(this.modal && this.mask.isVisible()){
29472             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29473             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29474         }
29475         if(this.isVisible()){
29476             this.constrainXY();
29477         }
29478     },
29479
29480     /**
29481      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29482      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29483      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29484      */
29485     destroy : function(removeEl){
29486         if(this.isVisible()){
29487             this.animateTarget = null;
29488             this.hide();
29489         }
29490         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29491         if(this.tabs){
29492             this.tabs.destroy(removeEl);
29493         }
29494         Roo.destroy(
29495              this.shim,
29496              this.proxy,
29497              this.resizer,
29498              this.close,
29499              this.mask
29500         );
29501         if(this.dd){
29502             this.dd.unreg();
29503         }
29504         if(this.buttons){
29505            for(var i = 0, len = this.buttons.length; i < len; i++){
29506                this.buttons[i].destroy();
29507            }
29508         }
29509         this.el.removeAllListeners();
29510         if(removeEl === true){
29511             this.el.update("");
29512             this.el.remove();
29513         }
29514         Roo.DialogManager.unregister(this);
29515     },
29516
29517     // private
29518     startMove : function(){
29519         if(this.proxyDrag){
29520             this.proxy.show();
29521         }
29522         if(this.constraintoviewport !== false){
29523             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29524         }
29525     },
29526
29527     // private
29528     endMove : function(){
29529         if(!this.proxyDrag){
29530             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29531         }else{
29532             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29533             this.proxy.hide();
29534         }
29535         this.refreshSize();
29536         this.adjustAssets();
29537         this.focus();
29538         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29539     },
29540
29541     /**
29542      * Brings this dialog to the front of any other visible dialogs
29543      * @return {Roo.BasicDialog} this
29544      */
29545     toFront : function(){
29546         Roo.DialogManager.bringToFront(this);
29547         return this;
29548     },
29549
29550     /**
29551      * Sends this dialog to the back (under) of any other visible dialogs
29552      * @return {Roo.BasicDialog} this
29553      */
29554     toBack : function(){
29555         Roo.DialogManager.sendToBack(this);
29556         return this;
29557     },
29558
29559     /**
29560      * Centers this dialog in the viewport
29561      * @return {Roo.BasicDialog} this
29562      */
29563     center : function(){
29564         var xy = this.el.getCenterXY(true);
29565         this.moveTo(xy[0], xy[1]);
29566         return this;
29567     },
29568
29569     /**
29570      * Moves the dialog's top-left corner to the specified point
29571      * @param {Number} x
29572      * @param {Number} y
29573      * @return {Roo.BasicDialog} this
29574      */
29575     moveTo : function(x, y){
29576         this.xy = [x,y];
29577         if(this.isVisible()){
29578             this.el.setXY(this.xy);
29579             this.adjustAssets();
29580         }
29581         return this;
29582     },
29583
29584     /**
29585      * Aligns the dialog to the specified element
29586      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29587      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29588      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29589      * @return {Roo.BasicDialog} this
29590      */
29591     alignTo : function(element, position, offsets){
29592         this.xy = this.el.getAlignToXY(element, position, offsets);
29593         if(this.isVisible()){
29594             this.el.setXY(this.xy);
29595             this.adjustAssets();
29596         }
29597         return this;
29598     },
29599
29600     /**
29601      * Anchors an element to another element and realigns it when the window is resized.
29602      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29603      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29604      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29605      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29606      * is a number, it is used as the buffer delay (defaults to 50ms).
29607      * @return {Roo.BasicDialog} this
29608      */
29609     anchorTo : function(el, alignment, offsets, monitorScroll){
29610         var action = function(){
29611             this.alignTo(el, alignment, offsets);
29612         };
29613         Roo.EventManager.onWindowResize(action, this);
29614         var tm = typeof monitorScroll;
29615         if(tm != 'undefined'){
29616             Roo.EventManager.on(window, 'scroll', action, this,
29617                 {buffer: tm == 'number' ? monitorScroll : 50});
29618         }
29619         action.call(this);
29620         return this;
29621     },
29622
29623     /**
29624      * Returns true if the dialog is visible
29625      * @return {Boolean}
29626      */
29627     isVisible : function(){
29628         return this.el.isVisible();
29629     },
29630
29631     // private
29632     animHide : function(callback){
29633         var b = Roo.get(this.animateTarget).getBox();
29634         this.proxy.show();
29635         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29636         this.el.hide();
29637         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29638                     this.hideEl.createDelegate(this, [callback]));
29639     },
29640
29641     /**
29642      * Hides the dialog.
29643      * @param {Function} callback (optional) Function to call when the dialog is hidden
29644      * @return {Roo.BasicDialog} this
29645      */
29646     hide : function(callback){
29647         if (this.fireEvent("beforehide", this) === false){
29648             return;
29649         }
29650         if(this.shadow){
29651             this.shadow.hide();
29652         }
29653         if(this.shim) {
29654           this.shim.hide();
29655         }
29656         // sometimes animateTarget seems to get set.. causing problems...
29657         // this just double checks..
29658         if(this.animateTarget && Roo.get(this.animateTarget)) {
29659            this.animHide(callback);
29660         }else{
29661             this.el.hide();
29662             this.hideEl(callback);
29663         }
29664         return this;
29665     },
29666
29667     // private
29668     hideEl : function(callback){
29669         this.proxy.hide();
29670         if(this.modal){
29671             this.mask.hide();
29672             Roo.get(document.body).removeClass("x-body-masked");
29673         }
29674         this.fireEvent("hide", this);
29675         if(typeof callback == "function"){
29676             callback();
29677         }
29678     },
29679
29680     // private
29681     hideAction : function(){
29682         this.setLeft("-10000px");
29683         this.setTop("-10000px");
29684         this.setStyle("visibility", "hidden");
29685     },
29686
29687     // private
29688     refreshSize : function(){
29689         this.size = this.el.getSize();
29690         this.xy = this.el.getXY();
29691         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29692     },
29693
29694     // private
29695     // z-index is managed by the DialogManager and may be overwritten at any time
29696     setZIndex : function(index){
29697         if(this.modal){
29698             this.mask.setStyle("z-index", index);
29699         }
29700         if(this.shim){
29701             this.shim.setStyle("z-index", ++index);
29702         }
29703         if(this.shadow){
29704             this.shadow.setZIndex(++index);
29705         }
29706         this.el.setStyle("z-index", ++index);
29707         if(this.proxy){
29708             this.proxy.setStyle("z-index", ++index);
29709         }
29710         if(this.resizer){
29711             this.resizer.proxy.setStyle("z-index", ++index);
29712         }
29713
29714         this.lastZIndex = index;
29715     },
29716
29717     /**
29718      * Returns the element for this dialog
29719      * @return {Roo.Element} The underlying dialog Element
29720      */
29721     getEl : function(){
29722         return this.el;
29723     }
29724 });
29725
29726 /**
29727  * @class Roo.DialogManager
29728  * Provides global access to BasicDialogs that have been created and
29729  * support for z-indexing (layering) multiple open dialogs.
29730  */
29731 Roo.DialogManager = function(){
29732     var list = {};
29733     var accessList = [];
29734     var front = null;
29735
29736     // private
29737     var sortDialogs = function(d1, d2){
29738         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29739     };
29740
29741     // private
29742     var orderDialogs = function(){
29743         accessList.sort(sortDialogs);
29744         var seed = Roo.DialogManager.zseed;
29745         for(var i = 0, len = accessList.length; i < len; i++){
29746             var dlg = accessList[i];
29747             if(dlg){
29748                 dlg.setZIndex(seed + (i*10));
29749             }
29750         }
29751     };
29752
29753     return {
29754         /**
29755          * The starting z-index for BasicDialogs (defaults to 9000)
29756          * @type Number The z-index value
29757          */
29758         zseed : 9000,
29759
29760         // private
29761         register : function(dlg){
29762             list[dlg.id] = dlg;
29763             accessList.push(dlg);
29764         },
29765
29766         // private
29767         unregister : function(dlg){
29768             delete list[dlg.id];
29769             var i=0;
29770             var len=0;
29771             if(!accessList.indexOf){
29772                 for(  i = 0, len = accessList.length; i < len; i++){
29773                     if(accessList[i] == dlg){
29774                         accessList.splice(i, 1);
29775                         return;
29776                     }
29777                 }
29778             }else{
29779                  i = accessList.indexOf(dlg);
29780                 if(i != -1){
29781                     accessList.splice(i, 1);
29782                 }
29783             }
29784         },
29785
29786         /**
29787          * Gets a registered dialog by id
29788          * @param {String/Object} id The id of the dialog or a dialog
29789          * @return {Roo.BasicDialog} this
29790          */
29791         get : function(id){
29792             return typeof id == "object" ? id : list[id];
29793         },
29794
29795         /**
29796          * Brings the specified dialog to the front
29797          * @param {String/Object} dlg The id of the dialog or a dialog
29798          * @return {Roo.BasicDialog} this
29799          */
29800         bringToFront : function(dlg){
29801             dlg = this.get(dlg);
29802             if(dlg != front){
29803                 front = dlg;
29804                 dlg._lastAccess = new Date().getTime();
29805                 orderDialogs();
29806             }
29807             return dlg;
29808         },
29809
29810         /**
29811          * Sends the specified dialog to the back
29812          * @param {String/Object} dlg The id of the dialog or a dialog
29813          * @return {Roo.BasicDialog} this
29814          */
29815         sendToBack : function(dlg){
29816             dlg = this.get(dlg);
29817             dlg._lastAccess = -(new Date().getTime());
29818             orderDialogs();
29819             return dlg;
29820         },
29821
29822         /**
29823          * Hides all dialogs
29824          */
29825         hideAll : function(){
29826             for(var id in list){
29827                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29828                     list[id].hide();
29829                 }
29830             }
29831         }
29832     };
29833 }();
29834
29835 /**
29836  * @class Roo.LayoutDialog
29837  * @extends Roo.BasicDialog
29838  * Dialog which provides adjustments for working with a layout in a Dialog.
29839  * Add your necessary layout config options to the dialog's config.<br>
29840  * Example usage (including a nested layout):
29841  * <pre><code>
29842 if(!dialog){
29843     dialog = new Roo.LayoutDialog("download-dlg", {
29844         modal: true,
29845         width:600,
29846         height:450,
29847         shadow:true,
29848         minWidth:500,
29849         minHeight:350,
29850         autoTabs:true,
29851         proxyDrag:true,
29852         // layout config merges with the dialog config
29853         center:{
29854             tabPosition: "top",
29855             alwaysShowTabs: true
29856         }
29857     });
29858     dialog.addKeyListener(27, dialog.hide, dialog);
29859     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29860     dialog.addButton("Build It!", this.getDownload, this);
29861
29862     // we can even add nested layouts
29863     var innerLayout = new Roo.BorderLayout("dl-inner", {
29864         east: {
29865             initialSize: 200,
29866             autoScroll:true,
29867             split:true
29868         },
29869         center: {
29870             autoScroll:true
29871         }
29872     });
29873     innerLayout.beginUpdate();
29874     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29875     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29876     innerLayout.endUpdate(true);
29877
29878     var layout = dialog.getLayout();
29879     layout.beginUpdate();
29880     layout.add("center", new Roo.ContentPanel("standard-panel",
29881                         {title: "Download the Source", fitToFrame:true}));
29882     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29883                {title: "Build your own roo.js"}));
29884     layout.getRegion("center").showPanel(sp);
29885     layout.endUpdate();
29886 }
29887 </code></pre>
29888     * @constructor
29889     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29890     * @param {Object} config configuration options
29891   */
29892 Roo.LayoutDialog = function(el, cfg){
29893     
29894     var config=  cfg;
29895     if (typeof(cfg) == 'undefined') {
29896         config = Roo.apply({}, el);
29897         // not sure why we use documentElement here.. - it should always be body.
29898         // IE7 borks horribly if we use documentElement.
29899         // webkit also does not like documentElement - it creates a body element...
29900         el = Roo.get( document.body || document.documentElement ).createChild();
29901         //config.autoCreate = true;
29902     }
29903     
29904     
29905     config.autoTabs = false;
29906     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29907     this.body.setStyle({overflow:"hidden", position:"relative"});
29908     this.layout = new Roo.BorderLayout(this.body.dom, config);
29909     this.layout.monitorWindowResize = false;
29910     this.el.addClass("x-dlg-auto-layout");
29911     // fix case when center region overwrites center function
29912     this.center = Roo.BasicDialog.prototype.center;
29913     this.on("show", this.layout.layout, this.layout, true);
29914     if (config.items) {
29915         var xitems = config.items;
29916         delete config.items;
29917         Roo.each(xitems, this.addxtype, this);
29918     }
29919     
29920     
29921 };
29922 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29923     /**
29924      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29925      * @deprecated
29926      */
29927     endUpdate : function(){
29928         this.layout.endUpdate();
29929     },
29930
29931     /**
29932      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29933      *  @deprecated
29934      */
29935     beginUpdate : function(){
29936         this.layout.beginUpdate();
29937     },
29938
29939     /**
29940      * Get the BorderLayout for this dialog
29941      * @return {Roo.BorderLayout}
29942      */
29943     getLayout : function(){
29944         return this.layout;
29945     },
29946
29947     showEl : function(){
29948         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29949         if(Roo.isIE7){
29950             this.layout.layout();
29951         }
29952     },
29953
29954     // private
29955     // Use the syncHeightBeforeShow config option to control this automatically
29956     syncBodyHeight : function(){
29957         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29958         if(this.layout){this.layout.layout();}
29959     },
29960     
29961       /**
29962      * Add an xtype element (actually adds to the layout.)
29963      * @return {Object} xdata xtype object data.
29964      */
29965     
29966     addxtype : function(c) {
29967         return this.layout.addxtype(c);
29968     }
29969 });/*
29970  * Based on:
29971  * Ext JS Library 1.1.1
29972  * Copyright(c) 2006-2007, Ext JS, LLC.
29973  *
29974  * Originally Released Under LGPL - original licence link has changed is not relivant.
29975  *
29976  * Fork - LGPL
29977  * <script type="text/javascript">
29978  */
29979  
29980 /**
29981  * @class Roo.MessageBox
29982  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29983  * Example usage:
29984  *<pre><code>
29985 // Basic alert:
29986 Roo.Msg.alert('Status', 'Changes saved successfully.');
29987
29988 // Prompt for user data:
29989 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29990     if (btn == 'ok'){
29991         // process text value...
29992     }
29993 });
29994
29995 // Show a dialog using config options:
29996 Roo.Msg.show({
29997    title:'Save Changes?',
29998    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29999    buttons: Roo.Msg.YESNOCANCEL,
30000    fn: processResult,
30001    animEl: 'elId'
30002 });
30003 </code></pre>
30004  * @singleton
30005  */
30006 Roo.MessageBox = function(){
30007     var dlg, opt, mask, waitTimer;
30008     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30009     var buttons, activeTextEl, bwidth;
30010
30011     // private
30012     var handleButton = function(button){
30013         dlg.hide();
30014         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30015     };
30016
30017     // private
30018     var handleHide = function(){
30019         if(opt && opt.cls){
30020             dlg.el.removeClass(opt.cls);
30021         }
30022         if(waitTimer){
30023             Roo.TaskMgr.stop(waitTimer);
30024             waitTimer = null;
30025         }
30026     };
30027
30028     // private
30029     var updateButtons = function(b){
30030         var width = 0;
30031         if(!b){
30032             buttons["ok"].hide();
30033             buttons["cancel"].hide();
30034             buttons["yes"].hide();
30035             buttons["no"].hide();
30036             dlg.footer.dom.style.display = 'none';
30037             return width;
30038         }
30039         dlg.footer.dom.style.display = '';
30040         for(var k in buttons){
30041             if(typeof buttons[k] != "function"){
30042                 if(b[k]){
30043                     buttons[k].show();
30044                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30045                     width += buttons[k].el.getWidth()+15;
30046                 }else{
30047                     buttons[k].hide();
30048                 }
30049             }
30050         }
30051         return width;
30052     };
30053
30054     // private
30055     var handleEsc = function(d, k, e){
30056         if(opt && opt.closable !== false){
30057             dlg.hide();
30058         }
30059         if(e){
30060             e.stopEvent();
30061         }
30062     };
30063
30064     return {
30065         /**
30066          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30067          * @return {Roo.BasicDialog} The BasicDialog element
30068          */
30069         getDialog : function(){
30070            if(!dlg){
30071                 dlg = new Roo.BasicDialog("x-msg-box", {
30072                     autoCreate : true,
30073                     shadow: true,
30074                     draggable: true,
30075                     resizable:false,
30076                     constraintoviewport:false,
30077                     fixedcenter:true,
30078                     collapsible : false,
30079                     shim:true,
30080                     modal: true,
30081                     width:400, height:100,
30082                     buttonAlign:"center",
30083                     closeClick : function(){
30084                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30085                             handleButton("no");
30086                         }else{
30087                             handleButton("cancel");
30088                         }
30089                     }
30090                 });
30091                 dlg.on("hide", handleHide);
30092                 mask = dlg.mask;
30093                 dlg.addKeyListener(27, handleEsc);
30094                 buttons = {};
30095                 var bt = this.buttonText;
30096                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30097                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30098                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30099                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30100                 bodyEl = dlg.body.createChild({
30101
30102                     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>'
30103                 });
30104                 msgEl = bodyEl.dom.firstChild;
30105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30106                 textboxEl.enableDisplayMode();
30107                 textboxEl.addKeyListener([10,13], function(){
30108                     if(dlg.isVisible() && opt && opt.buttons){
30109                         if(opt.buttons.ok){
30110                             handleButton("ok");
30111                         }else if(opt.buttons.yes){
30112                             handleButton("yes");
30113                         }
30114                     }
30115                 });
30116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30117                 textareaEl.enableDisplayMode();
30118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30119                 progressEl.enableDisplayMode();
30120                 var pf = progressEl.dom.firstChild;
30121                 if (pf) {
30122                     pp = Roo.get(pf.firstChild);
30123                     pp.setHeight(pf.offsetHeight);
30124                 }
30125                 
30126             }
30127             return dlg;
30128         },
30129
30130         /**
30131          * Updates the message box body text
30132          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30133          * the XHTML-compliant non-breaking space character '&amp;#160;')
30134          * @return {Roo.MessageBox} This message box
30135          */
30136         updateText : function(text){
30137             if(!dlg.isVisible() && !opt.width){
30138                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30139             }
30140             msgEl.innerHTML = text || '&#160;';
30141             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30142                         Math.max(opt.minWidth || this.minWidth, bwidth));
30143             if(opt.prompt){
30144                 activeTextEl.setWidth(w);
30145             }
30146             if(dlg.isVisible()){
30147                 dlg.fixedcenter = false;
30148             }
30149             dlg.setContentSize(w, bodyEl.getHeight());
30150             if(dlg.isVisible()){
30151                 dlg.fixedcenter = true;
30152             }
30153             return this;
30154         },
30155
30156         /**
30157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30161          * @return {Roo.MessageBox} This message box
30162          */
30163         updateProgress : function(value, text){
30164             if(text){
30165                 this.updateText(text);
30166             }
30167             if (pp) { // weird bug on my firefox - for some reason this is not defined
30168                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30169             }
30170             return this;
30171         },        
30172
30173         /**
30174          * Returns true if the message box is currently displayed
30175          * @return {Boolean} True if the message box is visible, else false
30176          */
30177         isVisible : function(){
30178             return dlg && dlg.isVisible();  
30179         },
30180
30181         /**
30182          * Hides the message box if it is displayed
30183          */
30184         hide : function(){
30185             if(this.isVisible()){
30186                 dlg.hide();
30187             }  
30188         },
30189
30190         /**
30191          * Displays a new message box, or reinitializes an existing message box, based on the config options
30192          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30193          * The following config object properties are supported:
30194          * <pre>
30195 Property    Type             Description
30196 ----------  ---------------  ------------------------------------------------------------------------------------
30197 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30198                                    closes (defaults to undefined)
30199 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30200                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30201 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30202                                    progress and wait dialogs will ignore this property and always hide the
30203                                    close button as they can only be closed programmatically.
30204 cls               String           A custom CSS class to apply to the message box element
30205 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30206                                    displayed (defaults to 75)
30207 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30208                                    function will be btn (the name of the button that was clicked, if applicable,
30209                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30210                                    Progress and wait dialogs will ignore this option since they do not respond to
30211                                    user actions and can only be closed programmatically, so any required function
30212                                    should be called by the same code after it closes the dialog.
30213 icon              String           A CSS class that provides a background image to be used as an icon for
30214                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30215 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30216 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30217 modal             Boolean          False to allow user interaction with the page while the message box is
30218                                    displayed (defaults to true)
30219 msg               String           A string that will replace the existing message box body text (defaults
30220                                    to the XHTML-compliant non-breaking space character '&#160;')
30221 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30222 progress          Boolean          True to display a progress bar (defaults to false)
30223 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30224 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30225 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30226 title             String           The title text
30227 value             String           The string value to set into the active textbox element if displayed
30228 wait              Boolean          True to display a progress bar (defaults to false)
30229 width             Number           The width of the dialog in pixels
30230 </pre>
30231          *
30232          * Example usage:
30233          * <pre><code>
30234 Roo.Msg.show({
30235    title: 'Address',
30236    msg: 'Please enter your address:',
30237    width: 300,
30238    buttons: Roo.MessageBox.OKCANCEL,
30239    multiline: true,
30240    fn: saveAddress,
30241    animEl: 'addAddressBtn'
30242 });
30243 </code></pre>
30244          * @param {Object} config Configuration options
30245          * @return {Roo.MessageBox} This message box
30246          */
30247         show : function(options)
30248         {
30249             
30250             // this causes nightmares if you show one dialog after another
30251             // especially on callbacks..
30252              
30253             if(this.isVisible()){
30254                 
30255                 this.hide();
30256                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30257                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30258                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30259                 
30260             }
30261             var d = this.getDialog();
30262             opt = options;
30263             d.setTitle(opt.title || "&#160;");
30264             d.close.setDisplayed(opt.closable !== false);
30265             activeTextEl = textboxEl;
30266             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30267             if(opt.prompt){
30268                 if(opt.multiline){
30269                     textboxEl.hide();
30270                     textareaEl.show();
30271                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30272                         opt.multiline : this.defaultTextHeight);
30273                     activeTextEl = textareaEl;
30274                 }else{
30275                     textboxEl.show();
30276                     textareaEl.hide();
30277                 }
30278             }else{
30279                 textboxEl.hide();
30280                 textareaEl.hide();
30281             }
30282             progressEl.setDisplayed(opt.progress === true);
30283             this.updateProgress(0);
30284             activeTextEl.dom.value = opt.value || "";
30285             if(opt.prompt){
30286                 dlg.setDefaultButton(activeTextEl);
30287             }else{
30288                 var bs = opt.buttons;
30289                 var db = null;
30290                 if(bs && bs.ok){
30291                     db = buttons["ok"];
30292                 }else if(bs && bs.yes){
30293                     db = buttons["yes"];
30294                 }
30295                 dlg.setDefaultButton(db);
30296             }
30297             bwidth = updateButtons(opt.buttons);
30298             this.updateText(opt.msg);
30299             if(opt.cls){
30300                 d.el.addClass(opt.cls);
30301             }
30302             d.proxyDrag = opt.proxyDrag === true;
30303             d.modal = opt.modal !== false;
30304             d.mask = opt.modal !== false ? mask : false;
30305             if(!d.isVisible()){
30306                 // force it to the end of the z-index stack so it gets a cursor in FF
30307                 document.body.appendChild(dlg.el.dom);
30308                 d.animateTarget = null;
30309                 d.show(options.animEl);
30310             }
30311             return this;
30312         },
30313
30314         /**
30315          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30316          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30317          * and closing the message box when the process is complete.
30318          * @param {String} title The title bar text
30319          * @param {String} msg The message box body text
30320          * @return {Roo.MessageBox} This message box
30321          */
30322         progress : function(title, msg){
30323             this.show({
30324                 title : title,
30325                 msg : msg,
30326                 buttons: false,
30327                 progress:true,
30328                 closable:false,
30329                 minWidth: this.minProgressWidth,
30330                 modal : true
30331             });
30332             return this;
30333         },
30334
30335         /**
30336          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30337          * If a callback function is passed it will be called after the user clicks the button, and the
30338          * id of the button that was clicked will be passed as the only parameter to the callback
30339          * (could also be the top-right close button).
30340          * @param {String} title The title bar text
30341          * @param {String} msg The message box body text
30342          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30343          * @param {Object} scope (optional) The scope of the callback function
30344          * @return {Roo.MessageBox} This message box
30345          */
30346         alert : function(title, msg, fn, scope){
30347             this.show({
30348                 title : title,
30349                 msg : msg,
30350                 buttons: this.OK,
30351                 fn: fn,
30352                 scope : scope,
30353                 modal : true
30354             });
30355             return this;
30356         },
30357
30358         /**
30359          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30360          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30361          * You are responsible for closing the message box when the process is complete.
30362          * @param {String} msg The message box body text
30363          * @param {String} title (optional) The title bar text
30364          * @return {Roo.MessageBox} This message box
30365          */
30366         wait : function(msg, title){
30367             this.show({
30368                 title : title,
30369                 msg : msg,
30370                 buttons: false,
30371                 closable:false,
30372                 progress:true,
30373                 modal:true,
30374                 width:300,
30375                 wait:true
30376             });
30377             waitTimer = Roo.TaskMgr.start({
30378                 run: function(i){
30379                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30380                 },
30381                 interval: 1000
30382             });
30383             return this;
30384         },
30385
30386         /**
30387          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30388          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30389          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30390          * @param {String} title The title bar text
30391          * @param {String} msg The message box body text
30392          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30393          * @param {Object} scope (optional) The scope of the callback function
30394          * @return {Roo.MessageBox} This message box
30395          */
30396         confirm : function(title, msg, fn, scope){
30397             this.show({
30398                 title : title,
30399                 msg : msg,
30400                 buttons: this.YESNO,
30401                 fn: fn,
30402                 scope : scope,
30403                 modal : true
30404             });
30405             return this;
30406         },
30407
30408         /**
30409          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30410          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30411          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30412          * (could also be the top-right close button) and the text that was entered will be passed as the two
30413          * parameters to the callback.
30414          * @param {String} title The title bar text
30415          * @param {String} msg The message box body text
30416          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30417          * @param {Object} scope (optional) The scope of the callback function
30418          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30419          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30420          * @return {Roo.MessageBox} This message box
30421          */
30422         prompt : function(title, msg, fn, scope, multiline){
30423             this.show({
30424                 title : title,
30425                 msg : msg,
30426                 buttons: this.OKCANCEL,
30427                 fn: fn,
30428                 minWidth:250,
30429                 scope : scope,
30430                 prompt:true,
30431                 multiline: multiline,
30432                 modal : true
30433             });
30434             return this;
30435         },
30436
30437         /**
30438          * Button config that displays a single OK button
30439          * @type Object
30440          */
30441         OK : {ok:true},
30442         /**
30443          * Button config that displays Yes and No buttons
30444          * @type Object
30445          */
30446         YESNO : {yes:true, no:true},
30447         /**
30448          * Button config that displays OK and Cancel buttons
30449          * @type Object
30450          */
30451         OKCANCEL : {ok:true, cancel:true},
30452         /**
30453          * Button config that displays Yes, No and Cancel buttons
30454          * @type Object
30455          */
30456         YESNOCANCEL : {yes:true, no:true, cancel:true},
30457
30458         /**
30459          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30460          * @type Number
30461          */
30462         defaultTextHeight : 75,
30463         /**
30464          * The maximum width in pixels of the message box (defaults to 600)
30465          * @type Number
30466          */
30467         maxWidth : 600,
30468         /**
30469          * The minimum width in pixels of the message box (defaults to 100)
30470          * @type Number
30471          */
30472         minWidth : 100,
30473         /**
30474          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30475          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30476          * @type Number
30477          */
30478         minProgressWidth : 250,
30479         /**
30480          * An object containing the default button text strings that can be overriden for localized language support.
30481          * Supported properties are: ok, cancel, yes and no.
30482          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30483          * @type Object
30484          */
30485         buttonText : {
30486             ok : "OK",
30487             cancel : "Cancel",
30488             yes : "Yes",
30489             no : "No"
30490         }
30491     };
30492 }();
30493
30494 /**
30495  * Shorthand for {@link Roo.MessageBox}
30496  */
30497 Roo.Msg = Roo.MessageBox;/*
30498  * Based on:
30499  * Ext JS Library 1.1.1
30500  * Copyright(c) 2006-2007, Ext JS, LLC.
30501  *
30502  * Originally Released Under LGPL - original licence link has changed is not relivant.
30503  *
30504  * Fork - LGPL
30505  * <script type="text/javascript">
30506  */
30507 /**
30508  * @class Roo.QuickTips
30509  * Provides attractive and customizable tooltips for any element.
30510  * @singleton
30511  */
30512 Roo.QuickTips = function(){
30513     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30514     var ce, bd, xy, dd;
30515     var visible = false, disabled = true, inited = false;
30516     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30517     
30518     var onOver = function(e){
30519         if(disabled){
30520             return;
30521         }
30522         var t = e.getTarget();
30523         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30524             return;
30525         }
30526         if(ce && t == ce.el){
30527             clearTimeout(hideProc);
30528             return;
30529         }
30530         if(t && tagEls[t.id]){
30531             tagEls[t.id].el = t;
30532             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30533             return;
30534         }
30535         var ttp, et = Roo.fly(t);
30536         var ns = cfg.namespace;
30537         if(tm.interceptTitles && t.title){
30538             ttp = t.title;
30539             t.qtip = ttp;
30540             t.removeAttribute("title");
30541             e.preventDefault();
30542         }else{
30543             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30544         }
30545         if(ttp){
30546             showProc = show.defer(tm.showDelay, tm, [{
30547                 el: t, 
30548                 text: ttp, 
30549                 width: et.getAttributeNS(ns, cfg.width),
30550                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30551                 title: et.getAttributeNS(ns, cfg.title),
30552                     cls: et.getAttributeNS(ns, cfg.cls)
30553             }]);
30554         }
30555     };
30556     
30557     var onOut = function(e){
30558         clearTimeout(showProc);
30559         var t = e.getTarget();
30560         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30561             hideProc = setTimeout(hide, tm.hideDelay);
30562         }
30563     };
30564     
30565     var onMove = function(e){
30566         if(disabled){
30567             return;
30568         }
30569         xy = e.getXY();
30570         xy[1] += 18;
30571         if(tm.trackMouse && ce){
30572             el.setXY(xy);
30573         }
30574     };
30575     
30576     var onDown = function(e){
30577         clearTimeout(showProc);
30578         clearTimeout(hideProc);
30579         if(!e.within(el)){
30580             if(tm.hideOnClick){
30581                 hide();
30582                 tm.disable();
30583                 tm.enable.defer(100, tm);
30584             }
30585         }
30586     };
30587     
30588     var getPad = function(){
30589         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30590     };
30591
30592     var show = function(o){
30593         if(disabled){
30594             return;
30595         }
30596         clearTimeout(dismissProc);
30597         ce = o;
30598         if(removeCls){ // in case manually hidden
30599             el.removeClass(removeCls);
30600             removeCls = null;
30601         }
30602         if(ce.cls){
30603             el.addClass(ce.cls);
30604             removeCls = ce.cls;
30605         }
30606         if(ce.title){
30607             tipTitle.update(ce.title);
30608             tipTitle.show();
30609         }else{
30610             tipTitle.update('');
30611             tipTitle.hide();
30612         }
30613         el.dom.style.width  = tm.maxWidth+'px';
30614         //tipBody.dom.style.width = '';
30615         tipBodyText.update(o.text);
30616         var p = getPad(), w = ce.width;
30617         if(!w){
30618             var td = tipBodyText.dom;
30619             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30620             if(aw > tm.maxWidth){
30621                 w = tm.maxWidth;
30622             }else if(aw < tm.minWidth){
30623                 w = tm.minWidth;
30624             }else{
30625                 w = aw;
30626             }
30627         }
30628         //tipBody.setWidth(w);
30629         el.setWidth(parseInt(w, 10) + p);
30630         if(ce.autoHide === false){
30631             close.setDisplayed(true);
30632             if(dd){
30633                 dd.unlock();
30634             }
30635         }else{
30636             close.setDisplayed(false);
30637             if(dd){
30638                 dd.lock();
30639             }
30640         }
30641         if(xy){
30642             el.avoidY = xy[1]-18;
30643             el.setXY(xy);
30644         }
30645         if(tm.animate){
30646             el.setOpacity(.1);
30647             el.setStyle("visibility", "visible");
30648             el.fadeIn({callback: afterShow});
30649         }else{
30650             afterShow();
30651         }
30652     };
30653     
30654     var afterShow = function(){
30655         if(ce){
30656             el.show();
30657             esc.enable();
30658             if(tm.autoDismiss && ce.autoHide !== false){
30659                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30660             }
30661         }
30662     };
30663     
30664     var hide = function(noanim){
30665         clearTimeout(dismissProc);
30666         clearTimeout(hideProc);
30667         ce = null;
30668         if(el.isVisible()){
30669             esc.disable();
30670             if(noanim !== true && tm.animate){
30671                 el.fadeOut({callback: afterHide});
30672             }else{
30673                 afterHide();
30674             } 
30675         }
30676     };
30677     
30678     var afterHide = function(){
30679         el.hide();
30680         if(removeCls){
30681             el.removeClass(removeCls);
30682             removeCls = null;
30683         }
30684     };
30685     
30686     return {
30687         /**
30688         * @cfg {Number} minWidth
30689         * The minimum width of the quick tip (defaults to 40)
30690         */
30691        minWidth : 40,
30692         /**
30693         * @cfg {Number} maxWidth
30694         * The maximum width of the quick tip (defaults to 300)
30695         */
30696        maxWidth : 300,
30697         /**
30698         * @cfg {Boolean} interceptTitles
30699         * True to automatically use the element's DOM title value if available (defaults to false)
30700         */
30701        interceptTitles : false,
30702         /**
30703         * @cfg {Boolean} trackMouse
30704         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30705         */
30706        trackMouse : false,
30707         /**
30708         * @cfg {Boolean} hideOnClick
30709         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30710         */
30711        hideOnClick : true,
30712         /**
30713         * @cfg {Number} showDelay
30714         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30715         */
30716        showDelay : 500,
30717         /**
30718         * @cfg {Number} hideDelay
30719         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30720         */
30721        hideDelay : 200,
30722         /**
30723         * @cfg {Boolean} autoHide
30724         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30725         * Used in conjunction with hideDelay.
30726         */
30727        autoHide : true,
30728         /**
30729         * @cfg {Boolean}
30730         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30731         * (defaults to true).  Used in conjunction with autoDismissDelay.
30732         */
30733        autoDismiss : true,
30734         /**
30735         * @cfg {Number}
30736         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30737         */
30738        autoDismissDelay : 5000,
30739        /**
30740         * @cfg {Boolean} animate
30741         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30742         */
30743        animate : false,
30744
30745        /**
30746         * @cfg {String} title
30747         * Title text to display (defaults to '').  This can be any valid HTML markup.
30748         */
30749         title: '',
30750        /**
30751         * @cfg {String} text
30752         * Body text to display (defaults to '').  This can be any valid HTML markup.
30753         */
30754         text : '',
30755        /**
30756         * @cfg {String} cls
30757         * A CSS class to apply to the base quick tip element (defaults to '').
30758         */
30759         cls : '',
30760        /**
30761         * @cfg {Number} width
30762         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30763         * minWidth or maxWidth.
30764         */
30765         width : null,
30766
30767     /**
30768      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30769      * or display QuickTips in a page.
30770      */
30771        init : function(){
30772           tm = Roo.QuickTips;
30773           cfg = tm.tagConfig;
30774           if(!inited){
30775               if(!Roo.isReady){ // allow calling of init() before onReady
30776                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30777                   return;
30778               }
30779               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30780               el.fxDefaults = {stopFx: true};
30781               // maximum custom styling
30782               //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>');
30783               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>');              
30784               tipTitle = el.child('h3');
30785               tipTitle.enableDisplayMode("block");
30786               tipBody = el.child('div.x-tip-bd');
30787               tipBodyText = el.child('div.x-tip-bd-inner');
30788               //bdLeft = el.child('div.x-tip-bd-left');
30789               //bdRight = el.child('div.x-tip-bd-right');
30790               close = el.child('div.x-tip-close');
30791               close.enableDisplayMode("block");
30792               close.on("click", hide);
30793               var d = Roo.get(document);
30794               d.on("mousedown", onDown);
30795               d.on("mouseover", onOver);
30796               d.on("mouseout", onOut);
30797               d.on("mousemove", onMove);
30798               esc = d.addKeyListener(27, hide);
30799               esc.disable();
30800               if(Roo.dd.DD){
30801                   dd = el.initDD("default", null, {
30802                       onDrag : function(){
30803                           el.sync();  
30804                       }
30805                   });
30806                   dd.setHandleElId(tipTitle.id);
30807                   dd.lock();
30808               }
30809               inited = true;
30810           }
30811           this.enable(); 
30812        },
30813
30814     /**
30815      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30816      * are supported:
30817      * <pre>
30818 Property    Type                   Description
30819 ----------  ---------------------  ------------------------------------------------------------------------
30820 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30821      * </ul>
30822      * @param {Object} config The config object
30823      */
30824        register : function(config){
30825            var cs = config instanceof Array ? config : arguments;
30826            for(var i = 0, len = cs.length; i < len; i++) {
30827                var c = cs[i];
30828                var target = c.target;
30829                if(target){
30830                    if(target instanceof Array){
30831                        for(var j = 0, jlen = target.length; j < jlen; j++){
30832                            tagEls[target[j]] = c;
30833                        }
30834                    }else{
30835                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30836                    }
30837                }
30838            }
30839        },
30840
30841     /**
30842      * Removes this quick tip from its element and destroys it.
30843      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30844      */
30845        unregister : function(el){
30846            delete tagEls[Roo.id(el)];
30847        },
30848
30849     /**
30850      * Enable this quick tip.
30851      */
30852        enable : function(){
30853            if(inited && disabled){
30854                locks.pop();
30855                if(locks.length < 1){
30856                    disabled = false;
30857                }
30858            }
30859        },
30860
30861     /**
30862      * Disable this quick tip.
30863      */
30864        disable : function(){
30865           disabled = true;
30866           clearTimeout(showProc);
30867           clearTimeout(hideProc);
30868           clearTimeout(dismissProc);
30869           if(ce){
30870               hide(true);
30871           }
30872           locks.push(1);
30873        },
30874
30875     /**
30876      * Returns true if the quick tip is enabled, else false.
30877      */
30878        isEnabled : function(){
30879             return !disabled;
30880        },
30881
30882         // private
30883        tagConfig : {
30884            namespace : "ext",
30885            attribute : "qtip",
30886            width : "width",
30887            target : "target",
30888            title : "qtitle",
30889            hide : "hide",
30890            cls : "qclass"
30891        }
30892    };
30893 }();
30894
30895 // backwards compat
30896 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30897  * Based on:
30898  * Ext JS Library 1.1.1
30899  * Copyright(c) 2006-2007, Ext JS, LLC.
30900  *
30901  * Originally Released Under LGPL - original licence link has changed is not relivant.
30902  *
30903  * Fork - LGPL
30904  * <script type="text/javascript">
30905  */
30906  
30907
30908 /**
30909  * @class Roo.tree.TreePanel
30910  * @extends Roo.data.Tree
30911
30912  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30913  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30914  * @cfg {Boolean} enableDD true to enable drag and drop
30915  * @cfg {Boolean} enableDrag true to enable just drag
30916  * @cfg {Boolean} enableDrop true to enable just drop
30917  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30918  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30919  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30920  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30921  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30922  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30923  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30924  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30925  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30926  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30927  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30928  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30929  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30930  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30931  * @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>
30932  * @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>
30933  * 
30934  * @constructor
30935  * @param {String/HTMLElement/Element} el The container element
30936  * @param {Object} config
30937  */
30938 Roo.tree.TreePanel = function(el, config){
30939     var root = false;
30940     var loader = false;
30941     if (config.root) {
30942         root = config.root;
30943         delete config.root;
30944     }
30945     if (config.loader) {
30946         loader = config.loader;
30947         delete config.loader;
30948     }
30949     
30950     Roo.apply(this, config);
30951     Roo.tree.TreePanel.superclass.constructor.call(this);
30952     this.el = Roo.get(el);
30953     this.el.addClass('x-tree');
30954     //console.log(root);
30955     if (root) {
30956         this.setRootNode( Roo.factory(root, Roo.tree));
30957     }
30958     if (loader) {
30959         this.loader = Roo.factory(loader, Roo.tree);
30960     }
30961    /**
30962     * Read-only. The id of the container element becomes this TreePanel's id.
30963     */
30964     this.id = this.el.id;
30965     this.addEvents({
30966         /**
30967         * @event beforeload
30968         * Fires before a node is loaded, return false to cancel
30969         * @param {Node} node The node being loaded
30970         */
30971         "beforeload" : true,
30972         /**
30973         * @event load
30974         * Fires when a node is loaded
30975         * @param {Node} node The node that was loaded
30976         */
30977         "load" : true,
30978         /**
30979         * @event textchange
30980         * Fires when the text for a node is changed
30981         * @param {Node} node The node
30982         * @param {String} text The new text
30983         * @param {String} oldText The old text
30984         */
30985         "textchange" : true,
30986         /**
30987         * @event beforeexpand
30988         * Fires before a node is expanded, return false to cancel.
30989         * @param {Node} node The node
30990         * @param {Boolean} deep
30991         * @param {Boolean} anim
30992         */
30993         "beforeexpand" : true,
30994         /**
30995         * @event beforecollapse
30996         * Fires before a node is collapsed, return false to cancel.
30997         * @param {Node} node The node
30998         * @param {Boolean} deep
30999         * @param {Boolean} anim
31000         */
31001         "beforecollapse" : true,
31002         /**
31003         * @event expand
31004         * Fires when a node is expanded
31005         * @param {Node} node The node
31006         */
31007         "expand" : true,
31008         /**
31009         * @event disabledchange
31010         * Fires when the disabled status of a node changes
31011         * @param {Node} node The node
31012         * @param {Boolean} disabled
31013         */
31014         "disabledchange" : true,
31015         /**
31016         * @event collapse
31017         * Fires when a node is collapsed
31018         * @param {Node} node The node
31019         */
31020         "collapse" : true,
31021         /**
31022         * @event beforeclick
31023         * Fires before click processing on a node. Return false to cancel the default action.
31024         * @param {Node} node The node
31025         * @param {Roo.EventObject} e The event object
31026         */
31027         "beforeclick":true,
31028         /**
31029         * @event checkchange
31030         * Fires when a node with a checkbox's checked property changes
31031         * @param {Node} this This node
31032         * @param {Boolean} checked
31033         */
31034         "checkchange":true,
31035         /**
31036         * @event click
31037         * Fires when a node is clicked
31038         * @param {Node} node The node
31039         * @param {Roo.EventObject} e The event object
31040         */
31041         "click":true,
31042         /**
31043         * @event dblclick
31044         * Fires when a node is double clicked
31045         * @param {Node} node The node
31046         * @param {Roo.EventObject} e The event object
31047         */
31048         "dblclick":true,
31049         /**
31050         * @event contextmenu
31051         * Fires when a node is right clicked
31052         * @param {Node} node The node
31053         * @param {Roo.EventObject} e The event object
31054         */
31055         "contextmenu":true,
31056         /**
31057         * @event beforechildrenrendered
31058         * Fires right before the child nodes for a node are rendered
31059         * @param {Node} node The node
31060         */
31061         "beforechildrenrendered":true,
31062         /**
31063         * @event startdrag
31064         * Fires when a node starts being dragged
31065         * @param {Roo.tree.TreePanel} this
31066         * @param {Roo.tree.TreeNode} node
31067         * @param {event} e The raw browser event
31068         */ 
31069        "startdrag" : true,
31070        /**
31071         * @event enddrag
31072         * Fires when a drag operation is complete
31073         * @param {Roo.tree.TreePanel} this
31074         * @param {Roo.tree.TreeNode} node
31075         * @param {event} e The raw browser event
31076         */
31077        "enddrag" : true,
31078        /**
31079         * @event dragdrop
31080         * Fires when a dragged node is dropped on a valid DD target
31081         * @param {Roo.tree.TreePanel} this
31082         * @param {Roo.tree.TreeNode} node
31083         * @param {DD} dd The dd it was dropped on
31084         * @param {event} e The raw browser event
31085         */
31086        "dragdrop" : true,
31087        /**
31088         * @event beforenodedrop
31089         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31090         * passed to handlers has the following properties:<br />
31091         * <ul style="padding:5px;padding-left:16px;">
31092         * <li>tree - The TreePanel</li>
31093         * <li>target - The node being targeted for the drop</li>
31094         * <li>data - The drag data from the drag source</li>
31095         * <li>point - The point of the drop - append, above or below</li>
31096         * <li>source - The drag source</li>
31097         * <li>rawEvent - Raw mouse event</li>
31098         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31099         * to be inserted by setting them on this object.</li>
31100         * <li>cancel - Set this to true to cancel the drop.</li>
31101         * </ul>
31102         * @param {Object} dropEvent
31103         */
31104        "beforenodedrop" : true,
31105        /**
31106         * @event nodedrop
31107         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31108         * passed to handlers has the following properties:<br />
31109         * <ul style="padding:5px;padding-left:16px;">
31110         * <li>tree - The TreePanel</li>
31111         * <li>target - The node being targeted for the drop</li>
31112         * <li>data - The drag data from the drag source</li>
31113         * <li>point - The point of the drop - append, above or below</li>
31114         * <li>source - The drag source</li>
31115         * <li>rawEvent - Raw mouse event</li>
31116         * <li>dropNode - Dropped node(s).</li>
31117         * </ul>
31118         * @param {Object} dropEvent
31119         */
31120        "nodedrop" : true,
31121         /**
31122         * @event nodedragover
31123         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31124         * passed to handlers has the following properties:<br />
31125         * <ul style="padding:5px;padding-left:16px;">
31126         * <li>tree - The TreePanel</li>
31127         * <li>target - The node being targeted for the drop</li>
31128         * <li>data - The drag data from the drag source</li>
31129         * <li>point - The point of the drop - append, above or below</li>
31130         * <li>source - The drag source</li>
31131         * <li>rawEvent - Raw mouse event</li>
31132         * <li>dropNode - Drop node(s) provided by the source.</li>
31133         * <li>cancel - Set this to true to signal drop not allowed.</li>
31134         * </ul>
31135         * @param {Object} dragOverEvent
31136         */
31137        "nodedragover" : true
31138         
31139     });
31140     if(this.singleExpand){
31141        this.on("beforeexpand", this.restrictExpand, this);
31142     }
31143     if (this.editor) {
31144         this.editor.tree = this;
31145         this.editor = Roo.factory(this.editor, Roo.tree);
31146     }
31147     
31148     if (this.selModel) {
31149         this.selModel = Roo.factory(this.selModel, Roo.tree);
31150     }
31151    
31152 };
31153 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31154     rootVisible : true,
31155     animate: Roo.enableFx,
31156     lines : true,
31157     enableDD : false,
31158     hlDrop : Roo.enableFx,
31159   
31160     renderer: false,
31161     
31162     rendererTip: false,
31163     // private
31164     restrictExpand : function(node){
31165         var p = node.parentNode;
31166         if(p){
31167             if(p.expandedChild && p.expandedChild.parentNode == p){
31168                 p.expandedChild.collapse();
31169             }
31170             p.expandedChild = node;
31171         }
31172     },
31173
31174     // private override
31175     setRootNode : function(node){
31176         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31177         if(!this.rootVisible){
31178             node.ui = new Roo.tree.RootTreeNodeUI(node);
31179         }
31180         return node;
31181     },
31182
31183     /**
31184      * Returns the container element for this TreePanel
31185      */
31186     getEl : function(){
31187         return this.el;
31188     },
31189
31190     /**
31191      * Returns the default TreeLoader for this TreePanel
31192      */
31193     getLoader : function(){
31194         return this.loader;
31195     },
31196
31197     /**
31198      * Expand all nodes
31199      */
31200     expandAll : function(){
31201         this.root.expand(true);
31202     },
31203
31204     /**
31205      * Collapse all nodes
31206      */
31207     collapseAll : function(){
31208         this.root.collapse(true);
31209     },
31210
31211     /**
31212      * Returns the selection model used by this TreePanel
31213      */
31214     getSelectionModel : function(){
31215         if(!this.selModel){
31216             this.selModel = new Roo.tree.DefaultSelectionModel();
31217         }
31218         return this.selModel;
31219     },
31220
31221     /**
31222      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31223      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31224      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31225      * @return {Array}
31226      */
31227     getChecked : function(a, startNode){
31228         startNode = startNode || this.root;
31229         var r = [];
31230         var f = function(){
31231             if(this.attributes.checked){
31232                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31233             }
31234         }
31235         startNode.cascade(f);
31236         return r;
31237     },
31238
31239     /**
31240      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31241      * @param {String} path
31242      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31243      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31244      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31245      */
31246     expandPath : function(path, attr, callback){
31247         attr = attr || "id";
31248         var keys = path.split(this.pathSeparator);
31249         var curNode = this.root;
31250         if(curNode.attributes[attr] != keys[1]){ // invalid root
31251             if(callback){
31252                 callback(false, null);
31253             }
31254             return;
31255         }
31256         var index = 1;
31257         var f = function(){
31258             if(++index == keys.length){
31259                 if(callback){
31260                     callback(true, curNode);
31261                 }
31262                 return;
31263             }
31264             var c = curNode.findChild(attr, keys[index]);
31265             if(!c){
31266                 if(callback){
31267                     callback(false, curNode);
31268                 }
31269                 return;
31270             }
31271             curNode = c;
31272             c.expand(false, false, f);
31273         };
31274         curNode.expand(false, false, f);
31275     },
31276
31277     /**
31278      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31279      * @param {String} path
31280      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31281      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31282      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31283      */
31284     selectPath : function(path, attr, callback){
31285         attr = attr || "id";
31286         var keys = path.split(this.pathSeparator);
31287         var v = keys.pop();
31288         if(keys.length > 0){
31289             var f = function(success, node){
31290                 if(success && node){
31291                     var n = node.findChild(attr, v);
31292                     if(n){
31293                         n.select();
31294                         if(callback){
31295                             callback(true, n);
31296                         }
31297                     }else if(callback){
31298                         callback(false, n);
31299                     }
31300                 }else{
31301                     if(callback){
31302                         callback(false, n);
31303                     }
31304                 }
31305             };
31306             this.expandPath(keys.join(this.pathSeparator), attr, f);
31307         }else{
31308             this.root.select();
31309             if(callback){
31310                 callback(true, this.root);
31311             }
31312         }
31313     },
31314
31315     getTreeEl : function(){
31316         return this.el;
31317     },
31318
31319     /**
31320      * Trigger rendering of this TreePanel
31321      */
31322     render : function(){
31323         if (this.innerCt) {
31324             return this; // stop it rendering more than once!!
31325         }
31326         
31327         this.innerCt = this.el.createChild({tag:"ul",
31328                cls:"x-tree-root-ct " +
31329                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31330
31331         if(this.containerScroll){
31332             Roo.dd.ScrollManager.register(this.el);
31333         }
31334         if((this.enableDD || this.enableDrop) && !this.dropZone){
31335            /**
31336             * The dropZone used by this tree if drop is enabled
31337             * @type Roo.tree.TreeDropZone
31338             */
31339              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31340                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31341            });
31342         }
31343         if((this.enableDD || this.enableDrag) && !this.dragZone){
31344            /**
31345             * The dragZone used by this tree if drag is enabled
31346             * @type Roo.tree.TreeDragZone
31347             */
31348             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31349                ddGroup: this.ddGroup || "TreeDD",
31350                scroll: this.ddScroll
31351            });
31352         }
31353         this.getSelectionModel().init(this);
31354         if (!this.root) {
31355             console.log("ROOT not set in tree");
31356             return;
31357         }
31358         this.root.render();
31359         if(!this.rootVisible){
31360             this.root.renderChildren();
31361         }
31362         return this;
31363     }
31364 });/*
31365  * Based on:
31366  * Ext JS Library 1.1.1
31367  * Copyright(c) 2006-2007, Ext JS, LLC.
31368  *
31369  * Originally Released Under LGPL - original licence link has changed is not relivant.
31370  *
31371  * Fork - LGPL
31372  * <script type="text/javascript">
31373  */
31374  
31375
31376 /**
31377  * @class Roo.tree.DefaultSelectionModel
31378  * @extends Roo.util.Observable
31379  * The default single selection for a TreePanel.
31380  * @param {Object} cfg Configuration
31381  */
31382 Roo.tree.DefaultSelectionModel = function(cfg){
31383    this.selNode = null;
31384    
31385    
31386    
31387    this.addEvents({
31388        /**
31389         * @event selectionchange
31390         * Fires when the selected node changes
31391         * @param {DefaultSelectionModel} this
31392         * @param {TreeNode} node the new selection
31393         */
31394        "selectionchange" : true,
31395
31396        /**
31397         * @event beforeselect
31398         * Fires before the selected node changes, return false to cancel the change
31399         * @param {DefaultSelectionModel} this
31400         * @param {TreeNode} node the new selection
31401         * @param {TreeNode} node the old selection
31402         */
31403        "beforeselect" : true
31404    });
31405    
31406     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31407 };
31408
31409 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31410     init : function(tree){
31411         this.tree = tree;
31412         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31413         tree.on("click", this.onNodeClick, this);
31414     },
31415     
31416     onNodeClick : function(node, e){
31417         if (e.ctrlKey && this.selNode == node)  {
31418             this.unselect(node);
31419             return;
31420         }
31421         this.select(node);
31422     },
31423     
31424     /**
31425      * Select a node.
31426      * @param {TreeNode} node The node to select
31427      * @return {TreeNode} The selected node
31428      */
31429     select : function(node){
31430         var last = this.selNode;
31431         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31432             if(last){
31433                 last.ui.onSelectedChange(false);
31434             }
31435             this.selNode = node;
31436             node.ui.onSelectedChange(true);
31437             this.fireEvent("selectionchange", this, node, last);
31438         }
31439         return node;
31440     },
31441     
31442     /**
31443      * Deselect a node.
31444      * @param {TreeNode} node The node to unselect
31445      */
31446     unselect : function(node){
31447         if(this.selNode == node){
31448             this.clearSelections();
31449         }    
31450     },
31451     
31452     /**
31453      * Clear all selections
31454      */
31455     clearSelections : function(){
31456         var n = this.selNode;
31457         if(n){
31458             n.ui.onSelectedChange(false);
31459             this.selNode = null;
31460             this.fireEvent("selectionchange", this, null);
31461         }
31462         return n;
31463     },
31464     
31465     /**
31466      * Get the selected node
31467      * @return {TreeNode} The selected node
31468      */
31469     getSelectedNode : function(){
31470         return this.selNode;    
31471     },
31472     
31473     /**
31474      * Returns true if the node is selected
31475      * @param {TreeNode} node The node to check
31476      * @return {Boolean}
31477      */
31478     isSelected : function(node){
31479         return this.selNode == node;  
31480     },
31481
31482     /**
31483      * Selects the node above the selected node in the tree, intelligently walking the nodes
31484      * @return TreeNode The new selection
31485      */
31486     selectPrevious : function(){
31487         var s = this.selNode || this.lastSelNode;
31488         if(!s){
31489             return null;
31490         }
31491         var ps = s.previousSibling;
31492         if(ps){
31493             if(!ps.isExpanded() || ps.childNodes.length < 1){
31494                 return this.select(ps);
31495             } else{
31496                 var lc = ps.lastChild;
31497                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31498                     lc = lc.lastChild;
31499                 }
31500                 return this.select(lc);
31501             }
31502         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31503             return this.select(s.parentNode);
31504         }
31505         return null;
31506     },
31507
31508     /**
31509      * Selects the node above the selected node in the tree, intelligently walking the nodes
31510      * @return TreeNode The new selection
31511      */
31512     selectNext : function(){
31513         var s = this.selNode || this.lastSelNode;
31514         if(!s){
31515             return null;
31516         }
31517         if(s.firstChild && s.isExpanded()){
31518              return this.select(s.firstChild);
31519          }else if(s.nextSibling){
31520              return this.select(s.nextSibling);
31521          }else if(s.parentNode){
31522             var newS = null;
31523             s.parentNode.bubble(function(){
31524                 if(this.nextSibling){
31525                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31526                     return false;
31527                 }
31528             });
31529             return newS;
31530          }
31531         return null;
31532     },
31533
31534     onKeyDown : function(e){
31535         var s = this.selNode || this.lastSelNode;
31536         // undesirable, but required
31537         var sm = this;
31538         if(!s){
31539             return;
31540         }
31541         var k = e.getKey();
31542         switch(k){
31543              case e.DOWN:
31544                  e.stopEvent();
31545                  this.selectNext();
31546              break;
31547              case e.UP:
31548                  e.stopEvent();
31549                  this.selectPrevious();
31550              break;
31551              case e.RIGHT:
31552                  e.preventDefault();
31553                  if(s.hasChildNodes()){
31554                      if(!s.isExpanded()){
31555                          s.expand();
31556                      }else if(s.firstChild){
31557                          this.select(s.firstChild, e);
31558                      }
31559                  }
31560              break;
31561              case e.LEFT:
31562                  e.preventDefault();
31563                  if(s.hasChildNodes() && s.isExpanded()){
31564                      s.collapse();
31565                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31566                      this.select(s.parentNode, e);
31567                  }
31568              break;
31569         };
31570     }
31571 });
31572
31573 /**
31574  * @class Roo.tree.MultiSelectionModel
31575  * @extends Roo.util.Observable
31576  * Multi selection for a TreePanel.
31577  * @param {Object} cfg Configuration
31578  */
31579 Roo.tree.MultiSelectionModel = function(){
31580    this.selNodes = [];
31581    this.selMap = {};
31582    this.addEvents({
31583        /**
31584         * @event selectionchange
31585         * Fires when the selected nodes change
31586         * @param {MultiSelectionModel} this
31587         * @param {Array} nodes Array of the selected nodes
31588         */
31589        "selectionchange" : true
31590    });
31591    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31592    
31593 };
31594
31595 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31596     init : function(tree){
31597         this.tree = tree;
31598         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31599         tree.on("click", this.onNodeClick, this);
31600     },
31601     
31602     onNodeClick : function(node, e){
31603         this.select(node, e, e.ctrlKey);
31604     },
31605     
31606     /**
31607      * Select a node.
31608      * @param {TreeNode} node The node to select
31609      * @param {EventObject} e (optional) An event associated with the selection
31610      * @param {Boolean} keepExisting True to retain existing selections
31611      * @return {TreeNode} The selected node
31612      */
31613     select : function(node, e, keepExisting){
31614         if(keepExisting !== true){
31615             this.clearSelections(true);
31616         }
31617         if(this.isSelected(node)){
31618             this.lastSelNode = node;
31619             return node;
31620         }
31621         this.selNodes.push(node);
31622         this.selMap[node.id] = node;
31623         this.lastSelNode = node;
31624         node.ui.onSelectedChange(true);
31625         this.fireEvent("selectionchange", this, this.selNodes);
31626         return node;
31627     },
31628     
31629     /**
31630      * Deselect a node.
31631      * @param {TreeNode} node The node to unselect
31632      */
31633     unselect : function(node){
31634         if(this.selMap[node.id]){
31635             node.ui.onSelectedChange(false);
31636             var sn = this.selNodes;
31637             var index = -1;
31638             if(sn.indexOf){
31639                 index = sn.indexOf(node);
31640             }else{
31641                 for(var i = 0, len = sn.length; i < len; i++){
31642                     if(sn[i] == node){
31643                         index = i;
31644                         break;
31645                     }
31646                 }
31647             }
31648             if(index != -1){
31649                 this.selNodes.splice(index, 1);
31650             }
31651             delete this.selMap[node.id];
31652             this.fireEvent("selectionchange", this, this.selNodes);
31653         }
31654     },
31655     
31656     /**
31657      * Clear all selections
31658      */
31659     clearSelections : function(suppressEvent){
31660         var sn = this.selNodes;
31661         if(sn.length > 0){
31662             for(var i = 0, len = sn.length; i < len; i++){
31663                 sn[i].ui.onSelectedChange(false);
31664             }
31665             this.selNodes = [];
31666             this.selMap = {};
31667             if(suppressEvent !== true){
31668                 this.fireEvent("selectionchange", this, this.selNodes);
31669             }
31670         }
31671     },
31672     
31673     /**
31674      * Returns true if the node is selected
31675      * @param {TreeNode} node The node to check
31676      * @return {Boolean}
31677      */
31678     isSelected : function(node){
31679         return this.selMap[node.id] ? true : false;  
31680     },
31681     
31682     /**
31683      * Returns an array of the selected nodes
31684      * @return {Array}
31685      */
31686     getSelectedNodes : function(){
31687         return this.selNodes;    
31688     },
31689
31690     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31691
31692     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31693
31694     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31695 });/*
31696  * Based on:
31697  * Ext JS Library 1.1.1
31698  * Copyright(c) 2006-2007, Ext JS, LLC.
31699  *
31700  * Originally Released Under LGPL - original licence link has changed is not relivant.
31701  *
31702  * Fork - LGPL
31703  * <script type="text/javascript">
31704  */
31705  
31706 /**
31707  * @class Roo.tree.TreeNode
31708  * @extends Roo.data.Node
31709  * @cfg {String} text The text for this node
31710  * @cfg {Boolean} expanded true to start the node expanded
31711  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31712  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31713  * @cfg {Boolean} disabled true to start the node disabled
31714  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31715  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31716  * @cfg {String} cls A css class to be added to the node
31717  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31718  * @cfg {String} href URL of the link used for the node (defaults to #)
31719  * @cfg {String} hrefTarget target frame for the link
31720  * @cfg {String} qtip An Ext QuickTip for the node
31721  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31722  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31723  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31724  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31725  * (defaults to undefined with no checkbox rendered)
31726  * @constructor
31727  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31728  */
31729 Roo.tree.TreeNode = function(attributes){
31730     attributes = attributes || {};
31731     if(typeof attributes == "string"){
31732         attributes = {text: attributes};
31733     }
31734     this.childrenRendered = false;
31735     this.rendered = false;
31736     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31737     this.expanded = attributes.expanded === true;
31738     this.isTarget = attributes.isTarget !== false;
31739     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31740     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31741
31742     /**
31743      * Read-only. The text for this node. To change it use setText().
31744      * @type String
31745      */
31746     this.text = attributes.text;
31747     /**
31748      * True if this node is disabled.
31749      * @type Boolean
31750      */
31751     this.disabled = attributes.disabled === true;
31752
31753     this.addEvents({
31754         /**
31755         * @event textchange
31756         * Fires when the text for this node is changed
31757         * @param {Node} this This node
31758         * @param {String} text The new text
31759         * @param {String} oldText The old text
31760         */
31761         "textchange" : true,
31762         /**
31763         * @event beforeexpand
31764         * Fires before this node is expanded, return false to cancel.
31765         * @param {Node} this This node
31766         * @param {Boolean} deep
31767         * @param {Boolean} anim
31768         */
31769         "beforeexpand" : true,
31770         /**
31771         * @event beforecollapse
31772         * Fires before this node is collapsed, return false to cancel.
31773         * @param {Node} this This node
31774         * @param {Boolean} deep
31775         * @param {Boolean} anim
31776         */
31777         "beforecollapse" : true,
31778         /**
31779         * @event expand
31780         * Fires when this node is expanded
31781         * @param {Node} this This node
31782         */
31783         "expand" : true,
31784         /**
31785         * @event disabledchange
31786         * Fires when the disabled status of this node changes
31787         * @param {Node} this This node
31788         * @param {Boolean} disabled
31789         */
31790         "disabledchange" : true,
31791         /**
31792         * @event collapse
31793         * Fires when this node is collapsed
31794         * @param {Node} this This node
31795         */
31796         "collapse" : true,
31797         /**
31798         * @event beforeclick
31799         * Fires before click processing. Return false to cancel the default action.
31800         * @param {Node} this This node
31801         * @param {Roo.EventObject} e The event object
31802         */
31803         "beforeclick":true,
31804         /**
31805         * @event checkchange
31806         * Fires when a node with a checkbox's checked property changes
31807         * @param {Node} this This node
31808         * @param {Boolean} checked
31809         */
31810         "checkchange":true,
31811         /**
31812         * @event click
31813         * Fires when this node is clicked
31814         * @param {Node} this This node
31815         * @param {Roo.EventObject} e The event object
31816         */
31817         "click":true,
31818         /**
31819         * @event dblclick
31820         * Fires when this node is double clicked
31821         * @param {Node} this This node
31822         * @param {Roo.EventObject} e The event object
31823         */
31824         "dblclick":true,
31825         /**
31826         * @event contextmenu
31827         * Fires when this node is right clicked
31828         * @param {Node} this This node
31829         * @param {Roo.EventObject} e The event object
31830         */
31831         "contextmenu":true,
31832         /**
31833         * @event beforechildrenrendered
31834         * Fires right before the child nodes for this node are rendered
31835         * @param {Node} this This node
31836         */
31837         "beforechildrenrendered":true
31838     });
31839
31840     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31841
31842     /**
31843      * Read-only. The UI for this node
31844      * @type TreeNodeUI
31845      */
31846     this.ui = new uiClass(this);
31847 };
31848 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31849     preventHScroll: true,
31850     /**
31851      * Returns true if this node is expanded
31852      * @return {Boolean}
31853      */
31854     isExpanded : function(){
31855         return this.expanded;
31856     },
31857
31858     /**
31859      * Returns the UI object for this node
31860      * @return {TreeNodeUI}
31861      */
31862     getUI : function(){
31863         return this.ui;
31864     },
31865
31866     // private override
31867     setFirstChild : function(node){
31868         var of = this.firstChild;
31869         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31870         if(this.childrenRendered && of && node != of){
31871             of.renderIndent(true, true);
31872         }
31873         if(this.rendered){
31874             this.renderIndent(true, true);
31875         }
31876     },
31877
31878     // private override
31879     setLastChild : function(node){
31880         var ol = this.lastChild;
31881         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31882         if(this.childrenRendered && ol && node != ol){
31883             ol.renderIndent(true, true);
31884         }
31885         if(this.rendered){
31886             this.renderIndent(true, true);
31887         }
31888     },
31889
31890     // these methods are overridden to provide lazy rendering support
31891     // private override
31892     appendChild : function(){
31893         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31894         if(node && this.childrenRendered){
31895             node.render();
31896         }
31897         this.ui.updateExpandIcon();
31898         return node;
31899     },
31900
31901     // private override
31902     removeChild : function(node){
31903         this.ownerTree.getSelectionModel().unselect(node);
31904         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31905         // if it's been rendered remove dom node
31906         if(this.childrenRendered){
31907             node.ui.remove();
31908         }
31909         if(this.childNodes.length < 1){
31910             this.collapse(false, false);
31911         }else{
31912             this.ui.updateExpandIcon();
31913         }
31914         if(!this.firstChild) {
31915             this.childrenRendered = false;
31916         }
31917         return node;
31918     },
31919
31920     // private override
31921     insertBefore : function(node, refNode){
31922         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31923         if(newNode && refNode && this.childrenRendered){
31924             node.render();
31925         }
31926         this.ui.updateExpandIcon();
31927         return newNode;
31928     },
31929
31930     /**
31931      * Sets the text for this node
31932      * @param {String} text
31933      */
31934     setText : function(text){
31935         var oldText = this.text;
31936         this.text = text;
31937         this.attributes.text = text;
31938         if(this.rendered){ // event without subscribing
31939             this.ui.onTextChange(this, text, oldText);
31940         }
31941         this.fireEvent("textchange", this, text, oldText);
31942     },
31943
31944     /**
31945      * Triggers selection of this node
31946      */
31947     select : function(){
31948         this.getOwnerTree().getSelectionModel().select(this);
31949     },
31950
31951     /**
31952      * Triggers deselection of this node
31953      */
31954     unselect : function(){
31955         this.getOwnerTree().getSelectionModel().unselect(this);
31956     },
31957
31958     /**
31959      * Returns true if this node is selected
31960      * @return {Boolean}
31961      */
31962     isSelected : function(){
31963         return this.getOwnerTree().getSelectionModel().isSelected(this);
31964     },
31965
31966     /**
31967      * Expand this node.
31968      * @param {Boolean} deep (optional) True to expand all children as well
31969      * @param {Boolean} anim (optional) false to cancel the default animation
31970      * @param {Function} callback (optional) A callback to be called when
31971      * expanding this node completes (does not wait for deep expand to complete).
31972      * Called with 1 parameter, this node.
31973      */
31974     expand : function(deep, anim, callback){
31975         if(!this.expanded){
31976             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31977                 return;
31978             }
31979             if(!this.childrenRendered){
31980                 this.renderChildren();
31981             }
31982             this.expanded = true;
31983             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31984                 this.ui.animExpand(function(){
31985                     this.fireEvent("expand", this);
31986                     if(typeof callback == "function"){
31987                         callback(this);
31988                     }
31989                     if(deep === true){
31990                         this.expandChildNodes(true);
31991                     }
31992                 }.createDelegate(this));
31993                 return;
31994             }else{
31995                 this.ui.expand();
31996                 this.fireEvent("expand", this);
31997                 if(typeof callback == "function"){
31998                     callback(this);
31999                 }
32000             }
32001         }else{
32002            if(typeof callback == "function"){
32003                callback(this);
32004            }
32005         }
32006         if(deep === true){
32007             this.expandChildNodes(true);
32008         }
32009     },
32010
32011     isHiddenRoot : function(){
32012         return this.isRoot && !this.getOwnerTree().rootVisible;
32013     },
32014
32015     /**
32016      * Collapse this node.
32017      * @param {Boolean} deep (optional) True to collapse all children as well
32018      * @param {Boolean} anim (optional) false to cancel the default animation
32019      */
32020     collapse : function(deep, anim){
32021         if(this.expanded && !this.isHiddenRoot()){
32022             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32023                 return;
32024             }
32025             this.expanded = false;
32026             if((this.getOwnerTree().animate && anim !== false) || anim){
32027                 this.ui.animCollapse(function(){
32028                     this.fireEvent("collapse", this);
32029                     if(deep === true){
32030                         this.collapseChildNodes(true);
32031                     }
32032                 }.createDelegate(this));
32033                 return;
32034             }else{
32035                 this.ui.collapse();
32036                 this.fireEvent("collapse", this);
32037             }
32038         }
32039         if(deep === true){
32040             var cs = this.childNodes;
32041             for(var i = 0, len = cs.length; i < len; i++) {
32042                 cs[i].collapse(true, false);
32043             }
32044         }
32045     },
32046
32047     // private
32048     delayedExpand : function(delay){
32049         if(!this.expandProcId){
32050             this.expandProcId = this.expand.defer(delay, this);
32051         }
32052     },
32053
32054     // private
32055     cancelExpand : function(){
32056         if(this.expandProcId){
32057             clearTimeout(this.expandProcId);
32058         }
32059         this.expandProcId = false;
32060     },
32061
32062     /**
32063      * Toggles expanded/collapsed state of the node
32064      */
32065     toggle : function(){
32066         if(this.expanded){
32067             this.collapse();
32068         }else{
32069             this.expand();
32070         }
32071     },
32072
32073     /**
32074      * Ensures all parent nodes are expanded
32075      */
32076     ensureVisible : function(callback){
32077         var tree = this.getOwnerTree();
32078         tree.expandPath(this.parentNode.getPath(), false, function(){
32079             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32080             Roo.callback(callback);
32081         }.createDelegate(this));
32082     },
32083
32084     /**
32085      * Expand all child nodes
32086      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32087      */
32088     expandChildNodes : function(deep){
32089         var cs = this.childNodes;
32090         for(var i = 0, len = cs.length; i < len; i++) {
32091                 cs[i].expand(deep);
32092         }
32093     },
32094
32095     /**
32096      * Collapse all child nodes
32097      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32098      */
32099     collapseChildNodes : function(deep){
32100         var cs = this.childNodes;
32101         for(var i = 0, len = cs.length; i < len; i++) {
32102                 cs[i].collapse(deep);
32103         }
32104     },
32105
32106     /**
32107      * Disables this node
32108      */
32109     disable : function(){
32110         this.disabled = true;
32111         this.unselect();
32112         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32113             this.ui.onDisableChange(this, true);
32114         }
32115         this.fireEvent("disabledchange", this, true);
32116     },
32117
32118     /**
32119      * Enables this node
32120      */
32121     enable : function(){
32122         this.disabled = false;
32123         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32124             this.ui.onDisableChange(this, false);
32125         }
32126         this.fireEvent("disabledchange", this, false);
32127     },
32128
32129     // private
32130     renderChildren : function(suppressEvent){
32131         if(suppressEvent !== false){
32132             this.fireEvent("beforechildrenrendered", this);
32133         }
32134         var cs = this.childNodes;
32135         for(var i = 0, len = cs.length; i < len; i++){
32136             cs[i].render(true);
32137         }
32138         this.childrenRendered = true;
32139     },
32140
32141     // private
32142     sort : function(fn, scope){
32143         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32144         if(this.childrenRendered){
32145             var cs = this.childNodes;
32146             for(var i = 0, len = cs.length; i < len; i++){
32147                 cs[i].render(true);
32148             }
32149         }
32150     },
32151
32152     // private
32153     render : function(bulkRender){
32154         this.ui.render(bulkRender);
32155         if(!this.rendered){
32156             this.rendered = true;
32157             if(this.expanded){
32158                 this.expanded = false;
32159                 this.expand(false, false);
32160             }
32161         }
32162     },
32163
32164     // private
32165     renderIndent : function(deep, refresh){
32166         if(refresh){
32167             this.ui.childIndent = null;
32168         }
32169         this.ui.renderIndent();
32170         if(deep === true && this.childrenRendered){
32171             var cs = this.childNodes;
32172             for(var i = 0, len = cs.length; i < len; i++){
32173                 cs[i].renderIndent(true, refresh);
32174             }
32175         }
32176     }
32177 });/*
32178  * Based on:
32179  * Ext JS Library 1.1.1
32180  * Copyright(c) 2006-2007, Ext JS, LLC.
32181  *
32182  * Originally Released Under LGPL - original licence link has changed is not relivant.
32183  *
32184  * Fork - LGPL
32185  * <script type="text/javascript">
32186  */
32187  
32188 /**
32189  * @class Roo.tree.AsyncTreeNode
32190  * @extends Roo.tree.TreeNode
32191  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32192  * @constructor
32193  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32194  */
32195  Roo.tree.AsyncTreeNode = function(config){
32196     this.loaded = false;
32197     this.loading = false;
32198     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32199     /**
32200     * @event beforeload
32201     * Fires before this node is loaded, return false to cancel
32202     * @param {Node} this This node
32203     */
32204     this.addEvents({'beforeload':true, 'load': true});
32205     /**
32206     * @event load
32207     * Fires when this node is loaded
32208     * @param {Node} this This node
32209     */
32210     /**
32211      * The loader used by this node (defaults to using the tree's defined loader)
32212      * @type TreeLoader
32213      * @property loader
32214      */
32215 };
32216 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32217     expand : function(deep, anim, callback){
32218         if(this.loading){ // if an async load is already running, waiting til it's done
32219             var timer;
32220             var f = function(){
32221                 if(!this.loading){ // done loading
32222                     clearInterval(timer);
32223                     this.expand(deep, anim, callback);
32224                 }
32225             }.createDelegate(this);
32226             timer = setInterval(f, 200);
32227             return;
32228         }
32229         if(!this.loaded){
32230             if(this.fireEvent("beforeload", this) === false){
32231                 return;
32232             }
32233             this.loading = true;
32234             this.ui.beforeLoad(this);
32235             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32236             if(loader){
32237                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32238                 return;
32239             }
32240         }
32241         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32242     },
32243     
32244     /**
32245      * Returns true if this node is currently loading
32246      * @return {Boolean}
32247      */
32248     isLoading : function(){
32249         return this.loading;  
32250     },
32251     
32252     loadComplete : function(deep, anim, callback){
32253         this.loading = false;
32254         this.loaded = true;
32255         this.ui.afterLoad(this);
32256         this.fireEvent("load", this);
32257         this.expand(deep, anim, callback);
32258     },
32259     
32260     /**
32261      * Returns true if this node has been loaded
32262      * @return {Boolean}
32263      */
32264     isLoaded : function(){
32265         return this.loaded;
32266     },
32267     
32268     hasChildNodes : function(){
32269         if(!this.isLeaf() && !this.loaded){
32270             return true;
32271         }else{
32272             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32273         }
32274     },
32275
32276     /**
32277      * Trigger a reload for this node
32278      * @param {Function} callback
32279      */
32280     reload : function(callback){
32281         this.collapse(false, false);
32282         while(this.firstChild){
32283             this.removeChild(this.firstChild);
32284         }
32285         this.childrenRendered = false;
32286         this.loaded = false;
32287         if(this.isHiddenRoot()){
32288             this.expanded = false;
32289         }
32290         this.expand(false, false, callback);
32291     }
32292 });/*
32293  * Based on:
32294  * Ext JS Library 1.1.1
32295  * Copyright(c) 2006-2007, Ext JS, LLC.
32296  *
32297  * Originally Released Under LGPL - original licence link has changed is not relivant.
32298  *
32299  * Fork - LGPL
32300  * <script type="text/javascript">
32301  */
32302  
32303 /**
32304  * @class Roo.tree.TreeNodeUI
32305  * @constructor
32306  * @param {Object} node The node to render
32307  * The TreeNode UI implementation is separate from the
32308  * tree implementation. Unless you are customizing the tree UI,
32309  * you should never have to use this directly.
32310  */
32311 Roo.tree.TreeNodeUI = function(node){
32312     this.node = node;
32313     this.rendered = false;
32314     this.animating = false;
32315     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32316 };
32317
32318 Roo.tree.TreeNodeUI.prototype = {
32319     removeChild : function(node){
32320         if(this.rendered){
32321             this.ctNode.removeChild(node.ui.getEl());
32322         }
32323     },
32324
32325     beforeLoad : function(){
32326          this.addClass("x-tree-node-loading");
32327     },
32328
32329     afterLoad : function(){
32330          this.removeClass("x-tree-node-loading");
32331     },
32332
32333     onTextChange : function(node, text, oldText){
32334         if(this.rendered){
32335             this.textNode.innerHTML = text;
32336         }
32337     },
32338
32339     onDisableChange : function(node, state){
32340         this.disabled = state;
32341         if(state){
32342             this.addClass("x-tree-node-disabled");
32343         }else{
32344             this.removeClass("x-tree-node-disabled");
32345         }
32346     },
32347
32348     onSelectedChange : function(state){
32349         if(state){
32350             this.focus();
32351             this.addClass("x-tree-selected");
32352         }else{
32353             //this.blur();
32354             this.removeClass("x-tree-selected");
32355         }
32356     },
32357
32358     onMove : function(tree, node, oldParent, newParent, index, refNode){
32359         this.childIndent = null;
32360         if(this.rendered){
32361             var targetNode = newParent.ui.getContainer();
32362             if(!targetNode){//target not rendered
32363                 this.holder = document.createElement("div");
32364                 this.holder.appendChild(this.wrap);
32365                 return;
32366             }
32367             var insertBefore = refNode ? refNode.ui.getEl() : null;
32368             if(insertBefore){
32369                 targetNode.insertBefore(this.wrap, insertBefore);
32370             }else{
32371                 targetNode.appendChild(this.wrap);
32372             }
32373             this.node.renderIndent(true);
32374         }
32375     },
32376
32377     addClass : function(cls){
32378         if(this.elNode){
32379             Roo.fly(this.elNode).addClass(cls);
32380         }
32381     },
32382
32383     removeClass : function(cls){
32384         if(this.elNode){
32385             Roo.fly(this.elNode).removeClass(cls);
32386         }
32387     },
32388
32389     remove : function(){
32390         if(this.rendered){
32391             this.holder = document.createElement("div");
32392             this.holder.appendChild(this.wrap);
32393         }
32394     },
32395
32396     fireEvent : function(){
32397         return this.node.fireEvent.apply(this.node, arguments);
32398     },
32399
32400     initEvents : function(){
32401         this.node.on("move", this.onMove, this);
32402         var E = Roo.EventManager;
32403         var a = this.anchor;
32404
32405         var el = Roo.fly(a, '_treeui');
32406
32407         if(Roo.isOpera){ // opera render bug ignores the CSS
32408             el.setStyle("text-decoration", "none");
32409         }
32410
32411         el.on("click", this.onClick, this);
32412         el.on("dblclick", this.onDblClick, this);
32413
32414         if(this.checkbox){
32415             Roo.EventManager.on(this.checkbox,
32416                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32417         }
32418
32419         el.on("contextmenu", this.onContextMenu, this);
32420
32421         var icon = Roo.fly(this.iconNode);
32422         icon.on("click", this.onClick, this);
32423         icon.on("dblclick", this.onDblClick, this);
32424         icon.on("contextmenu", this.onContextMenu, this);
32425         E.on(this.ecNode, "click", this.ecClick, this, true);
32426
32427         if(this.node.disabled){
32428             this.addClass("x-tree-node-disabled");
32429         }
32430         if(this.node.hidden){
32431             this.addClass("x-tree-node-disabled");
32432         }
32433         var ot = this.node.getOwnerTree();
32434         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32435         if(dd && (!this.node.isRoot || ot.rootVisible)){
32436             Roo.dd.Registry.register(this.elNode, {
32437                 node: this.node,
32438                 handles: this.getDDHandles(),
32439                 isHandle: false
32440             });
32441         }
32442     },
32443
32444     getDDHandles : function(){
32445         return [this.iconNode, this.textNode];
32446     },
32447
32448     hide : function(){
32449         if(this.rendered){
32450             this.wrap.style.display = "none";
32451         }
32452     },
32453
32454     show : function(){
32455         if(this.rendered){
32456             this.wrap.style.display = "";
32457         }
32458     },
32459
32460     onContextMenu : function(e){
32461         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32462             e.preventDefault();
32463             this.focus();
32464             this.fireEvent("contextmenu", this.node, e);
32465         }
32466     },
32467
32468     onClick : function(e){
32469         if(this.dropping){
32470             e.stopEvent();
32471             return;
32472         }
32473         if(this.fireEvent("beforeclick", this.node, e) !== false){
32474             if(!this.disabled && this.node.attributes.href){
32475                 this.fireEvent("click", this.node, e);
32476                 return;
32477             }
32478             e.preventDefault();
32479             if(this.disabled){
32480                 return;
32481             }
32482
32483             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32484                 this.node.toggle();
32485             }
32486
32487             this.fireEvent("click", this.node, e);
32488         }else{
32489             e.stopEvent();
32490         }
32491     },
32492
32493     onDblClick : function(e){
32494         e.preventDefault();
32495         if(this.disabled){
32496             return;
32497         }
32498         if(this.checkbox){
32499             this.toggleCheck();
32500         }
32501         if(!this.animating && this.node.hasChildNodes()){
32502             this.node.toggle();
32503         }
32504         this.fireEvent("dblclick", this.node, e);
32505     },
32506
32507     onCheckChange : function(){
32508         var checked = this.checkbox.checked;
32509         this.node.attributes.checked = checked;
32510         this.fireEvent('checkchange', this.node, checked);
32511     },
32512
32513     ecClick : function(e){
32514         if(!this.animating && this.node.hasChildNodes()){
32515             this.node.toggle();
32516         }
32517     },
32518
32519     startDrop : function(){
32520         this.dropping = true;
32521     },
32522
32523     // delayed drop so the click event doesn't get fired on a drop
32524     endDrop : function(){
32525        setTimeout(function(){
32526            this.dropping = false;
32527        }.createDelegate(this), 50);
32528     },
32529
32530     expand : function(){
32531         this.updateExpandIcon();
32532         this.ctNode.style.display = "";
32533     },
32534
32535     focus : function(){
32536         if(!this.node.preventHScroll){
32537             try{this.anchor.focus();
32538             }catch(e){}
32539         }else if(!Roo.isIE){
32540             try{
32541                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32542                 var l = noscroll.scrollLeft;
32543                 this.anchor.focus();
32544                 noscroll.scrollLeft = l;
32545             }catch(e){}
32546         }
32547     },
32548
32549     toggleCheck : function(value){
32550         var cb = this.checkbox;
32551         if(cb){
32552             cb.checked = (value === undefined ? !cb.checked : value);
32553         }
32554     },
32555
32556     blur : function(){
32557         try{
32558             this.anchor.blur();
32559         }catch(e){}
32560     },
32561
32562     animExpand : function(callback){
32563         var ct = Roo.get(this.ctNode);
32564         ct.stopFx();
32565         if(!this.node.hasChildNodes()){
32566             this.updateExpandIcon();
32567             this.ctNode.style.display = "";
32568             Roo.callback(callback);
32569             return;
32570         }
32571         this.animating = true;
32572         this.updateExpandIcon();
32573
32574         ct.slideIn('t', {
32575            callback : function(){
32576                this.animating = false;
32577                Roo.callback(callback);
32578             },
32579             scope: this,
32580             duration: this.node.ownerTree.duration || .25
32581         });
32582     },
32583
32584     highlight : function(){
32585         var tree = this.node.getOwnerTree();
32586         Roo.fly(this.wrap).highlight(
32587             tree.hlColor || "C3DAF9",
32588             {endColor: tree.hlBaseColor}
32589         );
32590     },
32591
32592     collapse : function(){
32593         this.updateExpandIcon();
32594         this.ctNode.style.display = "none";
32595     },
32596
32597     animCollapse : function(callback){
32598         var ct = Roo.get(this.ctNode);
32599         ct.enableDisplayMode('block');
32600         ct.stopFx();
32601
32602         this.animating = true;
32603         this.updateExpandIcon();
32604
32605         ct.slideOut('t', {
32606             callback : function(){
32607                this.animating = false;
32608                Roo.callback(callback);
32609             },
32610             scope: this,
32611             duration: this.node.ownerTree.duration || .25
32612         });
32613     },
32614
32615     getContainer : function(){
32616         return this.ctNode;
32617     },
32618
32619     getEl : function(){
32620         return this.wrap;
32621     },
32622
32623     appendDDGhost : function(ghostNode){
32624         ghostNode.appendChild(this.elNode.cloneNode(true));
32625     },
32626
32627     getDDRepairXY : function(){
32628         return Roo.lib.Dom.getXY(this.iconNode);
32629     },
32630
32631     onRender : function(){
32632         this.render();
32633     },
32634
32635     render : function(bulkRender){
32636         var n = this.node, a = n.attributes;
32637         var targetNode = n.parentNode ?
32638               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32639
32640         if(!this.rendered){
32641             this.rendered = true;
32642
32643             this.renderElements(n, a, targetNode, bulkRender);
32644
32645             if(a.qtip){
32646                if(this.textNode.setAttributeNS){
32647                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32648                    if(a.qtipTitle){
32649                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32650                    }
32651                }else{
32652                    this.textNode.setAttribute("ext:qtip", a.qtip);
32653                    if(a.qtipTitle){
32654                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32655                    }
32656                }
32657             }else if(a.qtipCfg){
32658                 a.qtipCfg.target = Roo.id(this.textNode);
32659                 Roo.QuickTips.register(a.qtipCfg);
32660             }
32661             this.initEvents();
32662             if(!this.node.expanded){
32663                 this.updateExpandIcon();
32664             }
32665         }else{
32666             if(bulkRender === true) {
32667                 targetNode.appendChild(this.wrap);
32668             }
32669         }
32670     },
32671
32672     renderElements : function(n, a, targetNode, bulkRender)
32673     {
32674         // add some indent caching, this helps performance when rendering a large tree
32675         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32676         var t = n.getOwnerTree();
32677         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32678         if (typeof(n.attributes.html) != 'undefined') {
32679             txt = n.attributes.html;
32680         }
32681         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32682         var cb = typeof a.checked == 'boolean';
32683         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32684         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32685             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32686             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32687             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32688             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32689             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32690              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32691                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32692             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32693             "</li>"];
32694
32695         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32696             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32697                                 n.nextSibling.ui.getEl(), buf.join(""));
32698         }else{
32699             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32700         }
32701
32702         this.elNode = this.wrap.childNodes[0];
32703         this.ctNode = this.wrap.childNodes[1];
32704         var cs = this.elNode.childNodes;
32705         this.indentNode = cs[0];
32706         this.ecNode = cs[1];
32707         this.iconNode = cs[2];
32708         var index = 3;
32709         if(cb){
32710             this.checkbox = cs[3];
32711             index++;
32712         }
32713         this.anchor = cs[index];
32714         this.textNode = cs[index].firstChild;
32715     },
32716
32717     getAnchor : function(){
32718         return this.anchor;
32719     },
32720
32721     getTextEl : function(){
32722         return this.textNode;
32723     },
32724
32725     getIconEl : function(){
32726         return this.iconNode;
32727     },
32728
32729     isChecked : function(){
32730         return this.checkbox ? this.checkbox.checked : false;
32731     },
32732
32733     updateExpandIcon : function(){
32734         if(this.rendered){
32735             var n = this.node, c1, c2;
32736             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32737             var hasChild = n.hasChildNodes();
32738             if(hasChild){
32739                 if(n.expanded){
32740                     cls += "-minus";
32741                     c1 = "x-tree-node-collapsed";
32742                     c2 = "x-tree-node-expanded";
32743                 }else{
32744                     cls += "-plus";
32745                     c1 = "x-tree-node-expanded";
32746                     c2 = "x-tree-node-collapsed";
32747                 }
32748                 if(this.wasLeaf){
32749                     this.removeClass("x-tree-node-leaf");
32750                     this.wasLeaf = false;
32751                 }
32752                 if(this.c1 != c1 || this.c2 != c2){
32753                     Roo.fly(this.elNode).replaceClass(c1, c2);
32754                     this.c1 = c1; this.c2 = c2;
32755                 }
32756             }else{
32757                 // this changes non-leafs into leafs if they have no children.
32758                 // it's not very rational behaviour..
32759                 
32760                 if(!this.wasLeaf && this.node.leaf){
32761                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32762                     delete this.c1;
32763                     delete this.c2;
32764                     this.wasLeaf = true;
32765                 }
32766             }
32767             var ecc = "x-tree-ec-icon "+cls;
32768             if(this.ecc != ecc){
32769                 this.ecNode.className = ecc;
32770                 this.ecc = ecc;
32771             }
32772         }
32773     },
32774
32775     getChildIndent : function(){
32776         if(!this.childIndent){
32777             var buf = [];
32778             var p = this.node;
32779             while(p){
32780                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32781                     if(!p.isLast()) {
32782                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32783                     } else {
32784                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32785                     }
32786                 }
32787                 p = p.parentNode;
32788             }
32789             this.childIndent = buf.join("");
32790         }
32791         return this.childIndent;
32792     },
32793
32794     renderIndent : function(){
32795         if(this.rendered){
32796             var indent = "";
32797             var p = this.node.parentNode;
32798             if(p){
32799                 indent = p.ui.getChildIndent();
32800             }
32801             if(this.indentMarkup != indent){ // don't rerender if not required
32802                 this.indentNode.innerHTML = indent;
32803                 this.indentMarkup = indent;
32804             }
32805             this.updateExpandIcon();
32806         }
32807     }
32808 };
32809
32810 Roo.tree.RootTreeNodeUI = function(){
32811     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32812 };
32813 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32814     render : function(){
32815         if(!this.rendered){
32816             var targetNode = this.node.ownerTree.innerCt.dom;
32817             this.node.expanded = true;
32818             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32819             this.wrap = this.ctNode = targetNode.firstChild;
32820         }
32821     },
32822     collapse : function(){
32823     },
32824     expand : function(){
32825     }
32826 });/*
32827  * Based on:
32828  * Ext JS Library 1.1.1
32829  * Copyright(c) 2006-2007, Ext JS, LLC.
32830  *
32831  * Originally Released Under LGPL - original licence link has changed is not relivant.
32832  *
32833  * Fork - LGPL
32834  * <script type="text/javascript">
32835  */
32836 /**
32837  * @class Roo.tree.TreeLoader
32838  * @extends Roo.util.Observable
32839  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32840  * nodes from a specified URL. The response must be a javascript Array definition
32841  * who's elements are node definition objects. eg:
32842  * <pre><code>
32843    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32844     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32845 </code></pre>
32846  * <br><br>
32847  * A server request is sent, and child nodes are loaded only when a node is expanded.
32848  * The loading node's id is passed to the server under the parameter name "node" to
32849  * enable the server to produce the correct child nodes.
32850  * <br><br>
32851  * To pass extra parameters, an event handler may be attached to the "beforeload"
32852  * event, and the parameters specified in the TreeLoader's baseParams property:
32853  * <pre><code>
32854     myTreeLoader.on("beforeload", function(treeLoader, node) {
32855         this.baseParams.category = node.attributes.category;
32856     }, this);
32857 </code></pre><
32858  * This would pass an HTTP parameter called "category" to the server containing
32859  * the value of the Node's "category" attribute.
32860  * @constructor
32861  * Creates a new Treeloader.
32862  * @param {Object} config A config object containing config properties.
32863  */
32864 Roo.tree.TreeLoader = function(config){
32865     this.baseParams = {};
32866     this.requestMethod = "POST";
32867     Roo.apply(this, config);
32868
32869     this.addEvents({
32870     
32871         /**
32872          * @event beforeload
32873          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32874          * @param {Object} This TreeLoader object.
32875          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32876          * @param {Object} callback The callback function specified in the {@link #load} call.
32877          */
32878         beforeload : true,
32879         /**
32880          * @event load
32881          * Fires when the node has been successfuly loaded.
32882          * @param {Object} This TreeLoader object.
32883          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32884          * @param {Object} response The response object containing the data from the server.
32885          */
32886         load : true,
32887         /**
32888          * @event loadexception
32889          * Fires if the network request failed.
32890          * @param {Object} This TreeLoader object.
32891          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32892          * @param {Object} response The response object containing the data from the server.
32893          */
32894         loadexception : true,
32895         /**
32896          * @event create
32897          * Fires before a node is created, enabling you to return custom Node types 
32898          * @param {Object} This TreeLoader object.
32899          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32900          */
32901         create : true
32902     });
32903
32904     Roo.tree.TreeLoader.superclass.constructor.call(this);
32905 };
32906
32907 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32908     /**
32909     * @cfg {String} dataUrl The URL from which to request a Json string which
32910     * specifies an array of node definition object representing the child nodes
32911     * to be loaded.
32912     */
32913     /**
32914     * @cfg {Object} baseParams (optional) An object containing properties which
32915     * specify HTTP parameters to be passed to each request for child nodes.
32916     */
32917     /**
32918     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32919     * created by this loader. If the attributes sent by the server have an attribute in this object,
32920     * they take priority.
32921     */
32922     /**
32923     * @cfg {Object} uiProviders (optional) An object containing properties which
32924     * 
32925     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32926     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32927     * <i>uiProvider</i> attribute of a returned child node is a string rather
32928     * than a reference to a TreeNodeUI implementation, this that string value
32929     * is used as a property name in the uiProviders object. You can define the provider named
32930     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32931     */
32932     uiProviders : {},
32933
32934     /**
32935     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32936     * child nodes before loading.
32937     */
32938     clearOnLoad : true,
32939
32940     /**
32941     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32942     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32943     * Grid query { data : [ .....] }
32944     */
32945     
32946     root : false,
32947      /**
32948     * @cfg {String} queryParam (optional) 
32949     * Name of the query as it will be passed on the querystring (defaults to 'node')
32950     * eg. the request will be ?node=[id]
32951     */
32952     
32953     
32954     queryParam: false,
32955     
32956     /**
32957      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32958      * This is called automatically when a node is expanded, but may be used to reload
32959      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32960      * @param {Roo.tree.TreeNode} node
32961      * @param {Function} callback
32962      */
32963     load : function(node, callback){
32964         if(this.clearOnLoad){
32965             while(node.firstChild){
32966                 node.removeChild(node.firstChild);
32967             }
32968         }
32969         if(node.attributes.children){ // preloaded json children
32970             var cs = node.attributes.children;
32971             for(var i = 0, len = cs.length; i < len; i++){
32972                 node.appendChild(this.createNode(cs[i]));
32973             }
32974             if(typeof callback == "function"){
32975                 callback();
32976             }
32977         }else if(this.dataUrl){
32978             this.requestData(node, callback);
32979         }
32980     },
32981
32982     getParams: function(node){
32983         var buf = [], bp = this.baseParams;
32984         for(var key in bp){
32985             if(typeof bp[key] != "function"){
32986                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32987             }
32988         }
32989         var n = this.queryParam === false ? 'node' : this.queryParam;
32990         buf.push(n + "=", encodeURIComponent(node.id));
32991         return buf.join("");
32992     },
32993
32994     requestData : function(node, callback){
32995         if(this.fireEvent("beforeload", this, node, callback) !== false){
32996             this.transId = Roo.Ajax.request({
32997                 method:this.requestMethod,
32998                 url: this.dataUrl||this.url,
32999                 success: this.handleResponse,
33000                 failure: this.handleFailure,
33001                 scope: this,
33002                 argument: {callback: callback, node: node},
33003                 params: this.getParams(node)
33004             });
33005         }else{
33006             // if the load is cancelled, make sure we notify
33007             // the node that we are done
33008             if(typeof callback == "function"){
33009                 callback();
33010             }
33011         }
33012     },
33013
33014     isLoading : function(){
33015         return this.transId ? true : false;
33016     },
33017
33018     abort : function(){
33019         if(this.isLoading()){
33020             Roo.Ajax.abort(this.transId);
33021         }
33022     },
33023
33024     // private
33025     createNode : function(attr)
33026     {
33027         // apply baseAttrs, nice idea Corey!
33028         if(this.baseAttrs){
33029             Roo.applyIf(attr, this.baseAttrs);
33030         }
33031         if(this.applyLoader !== false){
33032             attr.loader = this;
33033         }
33034         // uiProvider = depreciated..
33035         
33036         if(typeof(attr.uiProvider) == 'string'){
33037            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33038                 /**  eval:var:attr */ eval(attr.uiProvider);
33039         }
33040         if(typeof(this.uiProviders['default']) != 'undefined') {
33041             attr.uiProvider = this.uiProviders['default'];
33042         }
33043         
33044         this.fireEvent('create', this, attr);
33045         
33046         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33047         return(attr.leaf ?
33048                         new Roo.tree.TreeNode(attr) :
33049                         new Roo.tree.AsyncTreeNode(attr));
33050     },
33051
33052     processResponse : function(response, node, callback)
33053     {
33054         var json = response.responseText;
33055         try {
33056             
33057             var o = Roo.decode(json);
33058             
33059             if (!o.success) {
33060                 // it's a failure condition.
33061                 var a = response.argument;
33062                 this.fireEvent("loadexception", this, a.node, response);
33063                 Roo.log("Load failed - should have a handler really");
33064                 return;
33065             }
33066             
33067             if (this.root !== false) {
33068                 o = o[this.root];
33069             }
33070             
33071             for(var i = 0, len = o.length; i < len; i++){
33072                 var n = this.createNode(o[i]);
33073                 if(n){
33074                     node.appendChild(n);
33075                 }
33076             }
33077             if(typeof callback == "function"){
33078                 callback(this, node);
33079             }
33080         }catch(e){
33081             this.handleFailure(response);
33082         }
33083     },
33084
33085     handleResponse : function(response){
33086         this.transId = false;
33087         var a = response.argument;
33088         this.processResponse(response, a.node, a.callback);
33089         this.fireEvent("load", this, a.node, response);
33090     },
33091
33092     handleFailure : function(response)
33093     {
33094         // should handle failure better..
33095         this.transId = false;
33096         var a = response.argument;
33097         this.fireEvent("loadexception", this, a.node, response);
33098         if(typeof a.callback == "function"){
33099             a.callback(this, a.node);
33100         }
33101     }
33102 });/*
33103  * Based on:
33104  * Ext JS Library 1.1.1
33105  * Copyright(c) 2006-2007, Ext JS, LLC.
33106  *
33107  * Originally Released Under LGPL - original licence link has changed is not relivant.
33108  *
33109  * Fork - LGPL
33110  * <script type="text/javascript">
33111  */
33112
33113 /**
33114 * @class Roo.tree.TreeFilter
33115 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33116 * @param {TreePanel} tree
33117 * @param {Object} config (optional)
33118  */
33119 Roo.tree.TreeFilter = function(tree, config){
33120     this.tree = tree;
33121     this.filtered = {};
33122     Roo.apply(this, config);
33123 };
33124
33125 Roo.tree.TreeFilter.prototype = {
33126     clearBlank:false,
33127     reverse:false,
33128     autoClear:false,
33129     remove:false,
33130
33131      /**
33132      * Filter the data by a specific attribute.
33133      * @param {String/RegExp} value Either string that the attribute value
33134      * should start with or a RegExp to test against the attribute
33135      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33136      * @param {TreeNode} startNode (optional) The node to start the filter at.
33137      */
33138     filter : function(value, attr, startNode){
33139         attr = attr || "text";
33140         var f;
33141         if(typeof value == "string"){
33142             var vlen = value.length;
33143             // auto clear empty filter
33144             if(vlen == 0 && this.clearBlank){
33145                 this.clear();
33146                 return;
33147             }
33148             value = value.toLowerCase();
33149             f = function(n){
33150                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33151             };
33152         }else if(value.exec){ // regex?
33153             f = function(n){
33154                 return value.test(n.attributes[attr]);
33155             };
33156         }else{
33157             throw 'Illegal filter type, must be string or regex';
33158         }
33159         this.filterBy(f, null, startNode);
33160         },
33161
33162     /**
33163      * Filter by a function. The passed function will be called with each
33164      * node in the tree (or from the startNode). If the function returns true, the node is kept
33165      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33166      * @param {Function} fn The filter function
33167      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33168      */
33169     filterBy : function(fn, scope, startNode){
33170         startNode = startNode || this.tree.root;
33171         if(this.autoClear){
33172             this.clear();
33173         }
33174         var af = this.filtered, rv = this.reverse;
33175         var f = function(n){
33176             if(n == startNode){
33177                 return true;
33178             }
33179             if(af[n.id]){
33180                 return false;
33181             }
33182             var m = fn.call(scope || n, n);
33183             if(!m || rv){
33184                 af[n.id] = n;
33185                 n.ui.hide();
33186                 return false;
33187             }
33188             return true;
33189         };
33190         startNode.cascade(f);
33191         if(this.remove){
33192            for(var id in af){
33193                if(typeof id != "function"){
33194                    var n = af[id];
33195                    if(n && n.parentNode){
33196                        n.parentNode.removeChild(n);
33197                    }
33198                }
33199            }
33200         }
33201     },
33202
33203     /**
33204      * Clears the current filter. Note: with the "remove" option
33205      * set a filter cannot be cleared.
33206      */
33207     clear : function(){
33208         var t = this.tree;
33209         var af = this.filtered;
33210         for(var id in af){
33211             if(typeof id != "function"){
33212                 var n = af[id];
33213                 if(n){
33214                     n.ui.show();
33215                 }
33216             }
33217         }
33218         this.filtered = {};
33219     }
33220 };
33221 /*
33222  * Based on:
33223  * Ext JS Library 1.1.1
33224  * Copyright(c) 2006-2007, Ext JS, LLC.
33225  *
33226  * Originally Released Under LGPL - original licence link has changed is not relivant.
33227  *
33228  * Fork - LGPL
33229  * <script type="text/javascript">
33230  */
33231  
33232
33233 /**
33234  * @class Roo.tree.TreeSorter
33235  * Provides sorting of nodes in a TreePanel
33236  * 
33237  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33238  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33239  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33240  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33241  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33242  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33243  * @constructor
33244  * @param {TreePanel} tree
33245  * @param {Object} config
33246  */
33247 Roo.tree.TreeSorter = function(tree, config){
33248     Roo.apply(this, config);
33249     tree.on("beforechildrenrendered", this.doSort, this);
33250     tree.on("append", this.updateSort, this);
33251     tree.on("insert", this.updateSort, this);
33252     
33253     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33254     var p = this.property || "text";
33255     var sortType = this.sortType;
33256     var fs = this.folderSort;
33257     var cs = this.caseSensitive === true;
33258     var leafAttr = this.leafAttr || 'leaf';
33259
33260     this.sortFn = function(n1, n2){
33261         if(fs){
33262             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33263                 return 1;
33264             }
33265             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33266                 return -1;
33267             }
33268         }
33269         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33270         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33271         if(v1 < v2){
33272                         return dsc ? +1 : -1;
33273                 }else if(v1 > v2){
33274                         return dsc ? -1 : +1;
33275         }else{
33276                 return 0;
33277         }
33278     };
33279 };
33280
33281 Roo.tree.TreeSorter.prototype = {
33282     doSort : function(node){
33283         node.sort(this.sortFn);
33284     },
33285     
33286     compareNodes : function(n1, n2){
33287         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33288     },
33289     
33290     updateSort : function(tree, node){
33291         if(node.childrenRendered){
33292             this.doSort.defer(1, this, [node]);
33293         }
33294     }
33295 };/*
33296  * Based on:
33297  * Ext JS Library 1.1.1
33298  * Copyright(c) 2006-2007, Ext JS, LLC.
33299  *
33300  * Originally Released Under LGPL - original licence link has changed is not relivant.
33301  *
33302  * Fork - LGPL
33303  * <script type="text/javascript">
33304  */
33305
33306 if(Roo.dd.DropZone){
33307     
33308 Roo.tree.TreeDropZone = function(tree, config){
33309     this.allowParentInsert = false;
33310     this.allowContainerDrop = false;
33311     this.appendOnly = false;
33312     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33313     this.tree = tree;
33314     this.lastInsertClass = "x-tree-no-status";
33315     this.dragOverData = {};
33316 };
33317
33318 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33319     ddGroup : "TreeDD",
33320     
33321     expandDelay : 1000,
33322     
33323     expandNode : function(node){
33324         if(node.hasChildNodes() && !node.isExpanded()){
33325             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33326         }
33327     },
33328     
33329     queueExpand : function(node){
33330         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33331     },
33332     
33333     cancelExpand : function(){
33334         if(this.expandProcId){
33335             clearTimeout(this.expandProcId);
33336             this.expandProcId = false;
33337         }
33338     },
33339     
33340     isValidDropPoint : function(n, pt, dd, e, data){
33341         if(!n || !data){ return false; }
33342         var targetNode = n.node;
33343         var dropNode = data.node;
33344         // default drop rules
33345         if(!(targetNode && targetNode.isTarget && pt)){
33346             return false;
33347         }
33348         if(pt == "append" && targetNode.allowChildren === false){
33349             return false;
33350         }
33351         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33352             return false;
33353         }
33354         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33355             return false;
33356         }
33357         // reuse the object
33358         var overEvent = this.dragOverData;
33359         overEvent.tree = this.tree;
33360         overEvent.target = targetNode;
33361         overEvent.data = data;
33362         overEvent.point = pt;
33363         overEvent.source = dd;
33364         overEvent.rawEvent = e;
33365         overEvent.dropNode = dropNode;
33366         overEvent.cancel = false;  
33367         var result = this.tree.fireEvent("nodedragover", overEvent);
33368         return overEvent.cancel === false && result !== false;
33369     },
33370     
33371     getDropPoint : function(e, n, dd){
33372         var tn = n.node;
33373         if(tn.isRoot){
33374             return tn.allowChildren !== false ? "append" : false; // always append for root
33375         }
33376         var dragEl = n.ddel;
33377         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33378         var y = Roo.lib.Event.getPageY(e);
33379         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33380         
33381         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33382         var noAppend = tn.allowChildren === false;
33383         if(this.appendOnly || tn.parentNode.allowChildren === false){
33384             return noAppend ? false : "append";
33385         }
33386         var noBelow = false;
33387         if(!this.allowParentInsert){
33388             noBelow = tn.hasChildNodes() && tn.isExpanded();
33389         }
33390         var q = (b - t) / (noAppend ? 2 : 3);
33391         if(y >= t && y < (t + q)){
33392             return "above";
33393         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33394             return "below";
33395         }else{
33396             return "append";
33397         }
33398     },
33399     
33400     onNodeEnter : function(n, dd, e, data){
33401         this.cancelExpand();
33402     },
33403     
33404     onNodeOver : function(n, dd, e, data){
33405         var pt = this.getDropPoint(e, n, dd);
33406         var node = n.node;
33407         
33408         // auto node expand check
33409         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33410             this.queueExpand(node);
33411         }else if(pt != "append"){
33412             this.cancelExpand();
33413         }
33414         
33415         // set the insert point style on the target node
33416         var returnCls = this.dropNotAllowed;
33417         if(this.isValidDropPoint(n, pt, dd, e, data)){
33418            if(pt){
33419                var el = n.ddel;
33420                var cls;
33421                if(pt == "above"){
33422                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33423                    cls = "x-tree-drag-insert-above";
33424                }else if(pt == "below"){
33425                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33426                    cls = "x-tree-drag-insert-below";
33427                }else{
33428                    returnCls = "x-tree-drop-ok-append";
33429                    cls = "x-tree-drag-append";
33430                }
33431                if(this.lastInsertClass != cls){
33432                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33433                    this.lastInsertClass = cls;
33434                }
33435            }
33436        }
33437        return returnCls;
33438     },
33439     
33440     onNodeOut : function(n, dd, e, data){
33441         this.cancelExpand();
33442         this.removeDropIndicators(n);
33443     },
33444     
33445     onNodeDrop : function(n, dd, e, data){
33446         var point = this.getDropPoint(e, n, dd);
33447         var targetNode = n.node;
33448         targetNode.ui.startDrop();
33449         if(!this.isValidDropPoint(n, point, dd, e, data)){
33450             targetNode.ui.endDrop();
33451             return false;
33452         }
33453         // first try to find the drop node
33454         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33455         var dropEvent = {
33456             tree : this.tree,
33457             target: targetNode,
33458             data: data,
33459             point: point,
33460             source: dd,
33461             rawEvent: e,
33462             dropNode: dropNode,
33463             cancel: !dropNode   
33464         };
33465         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33466         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33467             targetNode.ui.endDrop();
33468             return false;
33469         }
33470         // allow target changing
33471         targetNode = dropEvent.target;
33472         if(point == "append" && !targetNode.isExpanded()){
33473             targetNode.expand(false, null, function(){
33474                 this.completeDrop(dropEvent);
33475             }.createDelegate(this));
33476         }else{
33477             this.completeDrop(dropEvent);
33478         }
33479         return true;
33480     },
33481     
33482     completeDrop : function(de){
33483         var ns = de.dropNode, p = de.point, t = de.target;
33484         if(!(ns instanceof Array)){
33485             ns = [ns];
33486         }
33487         var n;
33488         for(var i = 0, len = ns.length; i < len; i++){
33489             n = ns[i];
33490             if(p == "above"){
33491                 t.parentNode.insertBefore(n, t);
33492             }else if(p == "below"){
33493                 t.parentNode.insertBefore(n, t.nextSibling);
33494             }else{
33495                 t.appendChild(n);
33496             }
33497         }
33498         n.ui.focus();
33499         if(this.tree.hlDrop){
33500             n.ui.highlight();
33501         }
33502         t.ui.endDrop();
33503         this.tree.fireEvent("nodedrop", de);
33504     },
33505     
33506     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33507         if(this.tree.hlDrop){
33508             dropNode.ui.focus();
33509             dropNode.ui.highlight();
33510         }
33511         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33512     },
33513     
33514     getTree : function(){
33515         return this.tree;
33516     },
33517     
33518     removeDropIndicators : function(n){
33519         if(n && n.ddel){
33520             var el = n.ddel;
33521             Roo.fly(el).removeClass([
33522                     "x-tree-drag-insert-above",
33523                     "x-tree-drag-insert-below",
33524                     "x-tree-drag-append"]);
33525             this.lastInsertClass = "_noclass";
33526         }
33527     },
33528     
33529     beforeDragDrop : function(target, e, id){
33530         this.cancelExpand();
33531         return true;
33532     },
33533     
33534     afterRepair : function(data){
33535         if(data && Roo.enableFx){
33536             data.node.ui.highlight();
33537         }
33538         this.hideProxy();
33539     }    
33540 });
33541
33542 }
33543 /*
33544  * Based on:
33545  * Ext JS Library 1.1.1
33546  * Copyright(c) 2006-2007, Ext JS, LLC.
33547  *
33548  * Originally Released Under LGPL - original licence link has changed is not relivant.
33549  *
33550  * Fork - LGPL
33551  * <script type="text/javascript">
33552  */
33553  
33554
33555 if(Roo.dd.DragZone){
33556 Roo.tree.TreeDragZone = function(tree, config){
33557     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33558     this.tree = tree;
33559 };
33560
33561 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33562     ddGroup : "TreeDD",
33563     
33564     onBeforeDrag : function(data, e){
33565         var n = data.node;
33566         return n && n.draggable && !n.disabled;
33567     },
33568     
33569     onInitDrag : function(e){
33570         var data = this.dragData;
33571         this.tree.getSelectionModel().select(data.node);
33572         this.proxy.update("");
33573         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33574         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33575     },
33576     
33577     getRepairXY : function(e, data){
33578         return data.node.ui.getDDRepairXY();
33579     },
33580     
33581     onEndDrag : function(data, e){
33582         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33583     },
33584     
33585     onValidDrop : function(dd, e, id){
33586         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33587         this.hideProxy();
33588     },
33589     
33590     beforeInvalidDrop : function(e, id){
33591         // this scrolls the original position back into view
33592         var sm = this.tree.getSelectionModel();
33593         sm.clearSelections();
33594         sm.select(this.dragData.node);
33595     }
33596 });
33597 }/*
33598  * Based on:
33599  * Ext JS Library 1.1.1
33600  * Copyright(c) 2006-2007, Ext JS, LLC.
33601  *
33602  * Originally Released Under LGPL - original licence link has changed is not relivant.
33603  *
33604  * Fork - LGPL
33605  * <script type="text/javascript">
33606  */
33607 /**
33608  * @class Roo.tree.TreeEditor
33609  * @extends Roo.Editor
33610  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33611  * as the editor field.
33612  * @constructor
33613  * @param {Object} config (used to be the tree panel.)
33614  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33615  * 
33616  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33617  * @cfg {Roo.form.TextField|Object} field The field configuration
33618  *
33619  * 
33620  */
33621 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33622     var tree = config;
33623     var field;
33624     if (oldconfig) { // old style..
33625         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33626     } else {
33627         // new style..
33628         tree = config.tree;
33629         config.field = config.field  || {};
33630         config.field.xtype = 'TextField';
33631         field = Roo.factory(config.field, Roo.form);
33632     }
33633     config = config || {};
33634     
33635     
33636     this.addEvents({
33637         /**
33638          * @event beforenodeedit
33639          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33640          * false from the handler of this event.
33641          * @param {Editor} this
33642          * @param {Roo.tree.Node} node 
33643          */
33644         "beforenodeedit" : true
33645     });
33646     
33647     //Roo.log(config);
33648     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33649
33650     this.tree = tree;
33651
33652     tree.on('beforeclick', this.beforeNodeClick, this);
33653     tree.getTreeEl().on('mousedown', this.hide, this);
33654     this.on('complete', this.updateNode, this);
33655     this.on('beforestartedit', this.fitToTree, this);
33656     this.on('startedit', this.bindScroll, this, {delay:10});
33657     this.on('specialkey', this.onSpecialKey, this);
33658 };
33659
33660 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33661     /**
33662      * @cfg {String} alignment
33663      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33664      */
33665     alignment: "l-l",
33666     // inherit
33667     autoSize: false,
33668     /**
33669      * @cfg {Boolean} hideEl
33670      * True to hide the bound element while the editor is displayed (defaults to false)
33671      */
33672     hideEl : false,
33673     /**
33674      * @cfg {String} cls
33675      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33676      */
33677     cls: "x-small-editor x-tree-editor",
33678     /**
33679      * @cfg {Boolean} shim
33680      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33681      */
33682     shim:false,
33683     // inherit
33684     shadow:"frame",
33685     /**
33686      * @cfg {Number} maxWidth
33687      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33688      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33689      * scroll and client offsets into account prior to each edit.
33690      */
33691     maxWidth: 250,
33692
33693     editDelay : 350,
33694
33695     // private
33696     fitToTree : function(ed, el){
33697         var td = this.tree.getTreeEl().dom, nd = el.dom;
33698         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33699             td.scrollLeft = nd.offsetLeft;
33700         }
33701         var w = Math.min(
33702                 this.maxWidth,
33703                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33704         this.setSize(w, '');
33705         
33706         return this.fireEvent('beforenodeedit', this, this.editNode);
33707         
33708     },
33709
33710     // private
33711     triggerEdit : function(node){
33712         this.completeEdit();
33713         this.editNode = node;
33714         this.startEdit(node.ui.textNode, node.text);
33715     },
33716
33717     // private
33718     bindScroll : function(){
33719         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33720     },
33721
33722     // private
33723     beforeNodeClick : function(node, e){
33724         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33725         this.lastClick = new Date();
33726         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33727             e.stopEvent();
33728             this.triggerEdit(node);
33729             return false;
33730         }
33731         return true;
33732     },
33733
33734     // private
33735     updateNode : function(ed, value){
33736         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33737         this.editNode.setText(value);
33738     },
33739
33740     // private
33741     onHide : function(){
33742         Roo.tree.TreeEditor.superclass.onHide.call(this);
33743         if(this.editNode){
33744             this.editNode.ui.focus();
33745         }
33746     },
33747
33748     // private
33749     onSpecialKey : function(field, e){
33750         var k = e.getKey();
33751         if(k == e.ESC){
33752             e.stopEvent();
33753             this.cancelEdit();
33754         }else if(k == e.ENTER && !e.hasModifier()){
33755             e.stopEvent();
33756             this.completeEdit();
33757         }
33758     }
33759 });//<Script type="text/javascript">
33760 /*
33761  * Based on:
33762  * Ext JS Library 1.1.1
33763  * Copyright(c) 2006-2007, Ext JS, LLC.
33764  *
33765  * Originally Released Under LGPL - original licence link has changed is not relivant.
33766  *
33767  * Fork - LGPL
33768  * <script type="text/javascript">
33769  */
33770  
33771 /**
33772  * Not documented??? - probably should be...
33773  */
33774
33775 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33776     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33777     
33778     renderElements : function(n, a, targetNode, bulkRender){
33779         //consel.log("renderElements?");
33780         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33781
33782         var t = n.getOwnerTree();
33783         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33784         
33785         var cols = t.columns;
33786         var bw = t.borderWidth;
33787         var c = cols[0];
33788         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33789          var cb = typeof a.checked == "boolean";
33790         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33791         var colcls = 'x-t-' + tid + '-c0';
33792         var buf = [
33793             '<li class="x-tree-node">',
33794             
33795                 
33796                 '<div class="x-tree-node-el ', a.cls,'">',
33797                     // extran...
33798                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33799                 
33800                 
33801                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33802                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33803                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33804                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33805                            (a.iconCls ? ' '+a.iconCls : ''),
33806                            '" unselectable="on" />',
33807                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33808                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33809                              
33810                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33811                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33812                             '<span unselectable="on" qtip="' + tx + '">',
33813                              tx,
33814                              '</span></a>' ,
33815                     '</div>',
33816                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33817                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33818                  ];
33819         for(var i = 1, len = cols.length; i < len; i++){
33820             c = cols[i];
33821             colcls = 'x-t-' + tid + '-c' +i;
33822             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33823             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33824                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33825                       "</div>");
33826          }
33827          
33828          buf.push(
33829             '</a>',
33830             '<div class="x-clear"></div></div>',
33831             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33832             "</li>");
33833         
33834         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33835             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33836                                 n.nextSibling.ui.getEl(), buf.join(""));
33837         }else{
33838             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33839         }
33840         var el = this.wrap.firstChild;
33841         this.elRow = el;
33842         this.elNode = el.firstChild;
33843         this.ranchor = el.childNodes[1];
33844         this.ctNode = this.wrap.childNodes[1];
33845         var cs = el.firstChild.childNodes;
33846         this.indentNode = cs[0];
33847         this.ecNode = cs[1];
33848         this.iconNode = cs[2];
33849         var index = 3;
33850         if(cb){
33851             this.checkbox = cs[3];
33852             index++;
33853         }
33854         this.anchor = cs[index];
33855         
33856         this.textNode = cs[index].firstChild;
33857         
33858         //el.on("click", this.onClick, this);
33859         //el.on("dblclick", this.onDblClick, this);
33860         
33861         
33862        // console.log(this);
33863     },
33864     initEvents : function(){
33865         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33866         
33867             
33868         var a = this.ranchor;
33869
33870         var el = Roo.get(a);
33871
33872         if(Roo.isOpera){ // opera render bug ignores the CSS
33873             el.setStyle("text-decoration", "none");
33874         }
33875
33876         el.on("click", this.onClick, this);
33877         el.on("dblclick", this.onDblClick, this);
33878         el.on("contextmenu", this.onContextMenu, this);
33879         
33880     },
33881     
33882     /*onSelectedChange : function(state){
33883         if(state){
33884             this.focus();
33885             this.addClass("x-tree-selected");
33886         }else{
33887             //this.blur();
33888             this.removeClass("x-tree-selected");
33889         }
33890     },*/
33891     addClass : function(cls){
33892         if(this.elRow){
33893             Roo.fly(this.elRow).addClass(cls);
33894         }
33895         
33896     },
33897     
33898     
33899     removeClass : function(cls){
33900         if(this.elRow){
33901             Roo.fly(this.elRow).removeClass(cls);
33902         }
33903     }
33904
33905     
33906     
33907 });//<Script type="text/javascript">
33908
33909 /*
33910  * Based on:
33911  * Ext JS Library 1.1.1
33912  * Copyright(c) 2006-2007, Ext JS, LLC.
33913  *
33914  * Originally Released Under LGPL - original licence link has changed is not relivant.
33915  *
33916  * Fork - LGPL
33917  * <script type="text/javascript">
33918  */
33919  
33920
33921 /**
33922  * @class Roo.tree.ColumnTree
33923  * @extends Roo.data.TreePanel
33924  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33925  * @cfg {int} borderWidth  compined right/left border allowance
33926  * @constructor
33927  * @param {String/HTMLElement/Element} el The container element
33928  * @param {Object} config
33929  */
33930 Roo.tree.ColumnTree =  function(el, config)
33931 {
33932    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33933    this.addEvents({
33934         /**
33935         * @event resize
33936         * Fire this event on a container when it resizes
33937         * @param {int} w Width
33938         * @param {int} h Height
33939         */
33940        "resize" : true
33941     });
33942     this.on('resize', this.onResize, this);
33943 };
33944
33945 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33946     //lines:false,
33947     
33948     
33949     borderWidth: Roo.isBorderBox ? 0 : 2, 
33950     headEls : false,
33951     
33952     render : function(){
33953         // add the header.....
33954        
33955         Roo.tree.ColumnTree.superclass.render.apply(this);
33956         
33957         this.el.addClass('x-column-tree');
33958         
33959         this.headers = this.el.createChild(
33960             {cls:'x-tree-headers'},this.innerCt.dom);
33961    
33962         var cols = this.columns, c;
33963         var totalWidth = 0;
33964         this.headEls = [];
33965         var  len = cols.length;
33966         for(var i = 0; i < len; i++){
33967              c = cols[i];
33968              totalWidth += c.width;
33969             this.headEls.push(this.headers.createChild({
33970                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33971                  cn: {
33972                      cls:'x-tree-hd-text',
33973                      html: c.header
33974                  },
33975                  style:'width:'+(c.width-this.borderWidth)+'px;'
33976              }));
33977         }
33978         this.headers.createChild({cls:'x-clear'});
33979         // prevent floats from wrapping when clipped
33980         this.headers.setWidth(totalWidth);
33981         //this.innerCt.setWidth(totalWidth);
33982         this.innerCt.setStyle({ overflow: 'auto' });
33983         this.onResize(this.width, this.height);
33984              
33985         
33986     },
33987     onResize : function(w,h)
33988     {
33989         this.height = h;
33990         this.width = w;
33991         // resize cols..
33992         this.innerCt.setWidth(this.width);
33993         this.innerCt.setHeight(this.height-20);
33994         
33995         // headers...
33996         var cols = this.columns, c;
33997         var totalWidth = 0;
33998         var expEl = false;
33999         var len = cols.length;
34000         for(var i = 0; i < len; i++){
34001             c = cols[i];
34002             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34003                 // it's the expander..
34004                 expEl  = this.headEls[i];
34005                 continue;
34006             }
34007             totalWidth += c.width;
34008             
34009         }
34010         if (expEl) {
34011             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34012         }
34013         this.headers.setWidth(w-20);
34014
34015         
34016         
34017         
34018     }
34019 });
34020 /*
34021  * Based on:
34022  * Ext JS Library 1.1.1
34023  * Copyright(c) 2006-2007, Ext JS, LLC.
34024  *
34025  * Originally Released Under LGPL - original licence link has changed is not relivant.
34026  *
34027  * Fork - LGPL
34028  * <script type="text/javascript">
34029  */
34030  
34031 /**
34032  * @class Roo.menu.Menu
34033  * @extends Roo.util.Observable
34034  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34035  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34036  * @constructor
34037  * Creates a new Menu
34038  * @param {Object} config Configuration options
34039  */
34040 Roo.menu.Menu = function(config){
34041     Roo.apply(this, config);
34042     this.id = this.id || Roo.id();
34043     this.addEvents({
34044         /**
34045          * @event beforeshow
34046          * Fires before this menu is displayed
34047          * @param {Roo.menu.Menu} this
34048          */
34049         beforeshow : true,
34050         /**
34051          * @event beforehide
34052          * Fires before this menu is hidden
34053          * @param {Roo.menu.Menu} this
34054          */
34055         beforehide : true,
34056         /**
34057          * @event show
34058          * Fires after this menu is displayed
34059          * @param {Roo.menu.Menu} this
34060          */
34061         show : true,
34062         /**
34063          * @event hide
34064          * Fires after this menu is hidden
34065          * @param {Roo.menu.Menu} this
34066          */
34067         hide : true,
34068         /**
34069          * @event click
34070          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34071          * @param {Roo.menu.Menu} this
34072          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34073          * @param {Roo.EventObject} e
34074          */
34075         click : true,
34076         /**
34077          * @event mouseover
34078          * Fires when the mouse is hovering over this menu
34079          * @param {Roo.menu.Menu} this
34080          * @param {Roo.EventObject} e
34081          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34082          */
34083         mouseover : true,
34084         /**
34085          * @event mouseout
34086          * Fires when the mouse exits this menu
34087          * @param {Roo.menu.Menu} this
34088          * @param {Roo.EventObject} e
34089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34090          */
34091         mouseout : true,
34092         /**
34093          * @event itemclick
34094          * Fires when a menu item contained in this menu is clicked
34095          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34096          * @param {Roo.EventObject} e
34097          */
34098         itemclick: true
34099     });
34100     if (this.registerMenu) {
34101         Roo.menu.MenuMgr.register(this);
34102     }
34103     
34104     var mis = this.items;
34105     this.items = new Roo.util.MixedCollection();
34106     if(mis){
34107         this.add.apply(this, mis);
34108     }
34109 };
34110
34111 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34112     /**
34113      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34114      */
34115     minWidth : 120,
34116     /**
34117      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34118      * for bottom-right shadow (defaults to "sides")
34119      */
34120     shadow : "sides",
34121     /**
34122      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34123      * this menu (defaults to "tl-tr?")
34124      */
34125     subMenuAlign : "tl-tr?",
34126     /**
34127      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34128      * relative to its element of origin (defaults to "tl-bl?")
34129      */
34130     defaultAlign : "tl-bl?",
34131     /**
34132      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34133      */
34134     allowOtherMenus : false,
34135     /**
34136      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34137      */
34138     registerMenu : true,
34139
34140     hidden:true,
34141
34142     // private
34143     render : function(){
34144         if(this.el){
34145             return;
34146         }
34147         var el = this.el = new Roo.Layer({
34148             cls: "x-menu",
34149             shadow:this.shadow,
34150             constrain: false,
34151             parentEl: this.parentEl || document.body,
34152             zindex:15000
34153         });
34154
34155         this.keyNav = new Roo.menu.MenuNav(this);
34156
34157         if(this.plain){
34158             el.addClass("x-menu-plain");
34159         }
34160         if(this.cls){
34161             el.addClass(this.cls);
34162         }
34163         // generic focus element
34164         this.focusEl = el.createChild({
34165             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34166         });
34167         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34168         ul.on("click", this.onClick, this);
34169         ul.on("mouseover", this.onMouseOver, this);
34170         ul.on("mouseout", this.onMouseOut, this);
34171         this.items.each(function(item){
34172             var li = document.createElement("li");
34173             li.className = "x-menu-list-item";
34174             ul.dom.appendChild(li);
34175             item.render(li, this);
34176         }, this);
34177         this.ul = ul;
34178         this.autoWidth();
34179     },
34180
34181     // private
34182     autoWidth : function(){
34183         var el = this.el, ul = this.ul;
34184         if(!el){
34185             return;
34186         }
34187         var w = this.width;
34188         if(w){
34189             el.setWidth(w);
34190         }else if(Roo.isIE){
34191             el.setWidth(this.minWidth);
34192             var t = el.dom.offsetWidth; // force recalc
34193             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34194         }
34195     },
34196
34197     // private
34198     delayAutoWidth : function(){
34199         if(this.rendered){
34200             if(!this.awTask){
34201                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34202             }
34203             this.awTask.delay(20);
34204         }
34205     },
34206
34207     // private
34208     findTargetItem : function(e){
34209         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34210         if(t && t.menuItemId){
34211             return this.items.get(t.menuItemId);
34212         }
34213     },
34214
34215     // private
34216     onClick : function(e){
34217         var t;
34218         if(t = this.findTargetItem(e)){
34219             t.onClick(e);
34220             this.fireEvent("click", this, t, e);
34221         }
34222     },
34223
34224     // private
34225     setActiveItem : function(item, autoExpand){
34226         if(item != this.activeItem){
34227             if(this.activeItem){
34228                 this.activeItem.deactivate();
34229             }
34230             this.activeItem = item;
34231             item.activate(autoExpand);
34232         }else if(autoExpand){
34233             item.expandMenu();
34234         }
34235     },
34236
34237     // private
34238     tryActivate : function(start, step){
34239         var items = this.items;
34240         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34241             var item = items.get(i);
34242             if(!item.disabled && item.canActivate){
34243                 this.setActiveItem(item, false);
34244                 return item;
34245             }
34246         }
34247         return false;
34248     },
34249
34250     // private
34251     onMouseOver : function(e){
34252         var t;
34253         if(t = this.findTargetItem(e)){
34254             if(t.canActivate && !t.disabled){
34255                 this.setActiveItem(t, true);
34256             }
34257         }
34258         this.fireEvent("mouseover", this, e, t);
34259     },
34260
34261     // private
34262     onMouseOut : function(e){
34263         var t;
34264         if(t = this.findTargetItem(e)){
34265             if(t == this.activeItem && t.shouldDeactivate(e)){
34266                 this.activeItem.deactivate();
34267                 delete this.activeItem;
34268             }
34269         }
34270         this.fireEvent("mouseout", this, e, t);
34271     },
34272
34273     /**
34274      * Read-only.  Returns true if the menu is currently displayed, else false.
34275      * @type Boolean
34276      */
34277     isVisible : function(){
34278         return this.el && !this.hidden;
34279     },
34280
34281     /**
34282      * Displays this menu relative to another element
34283      * @param {String/HTMLElement/Roo.Element} element The element to align to
34284      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34285      * the element (defaults to this.defaultAlign)
34286      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34287      */
34288     show : function(el, pos, parentMenu){
34289         this.parentMenu = parentMenu;
34290         if(!this.el){
34291             this.render();
34292         }
34293         this.fireEvent("beforeshow", this);
34294         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34295     },
34296
34297     /**
34298      * Displays this menu at a specific xy position
34299      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34300      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34301      */
34302     showAt : function(xy, parentMenu, /* private: */_e){
34303         this.parentMenu = parentMenu;
34304         if(!this.el){
34305             this.render();
34306         }
34307         if(_e !== false){
34308             this.fireEvent("beforeshow", this);
34309             xy = this.el.adjustForConstraints(xy);
34310         }
34311         this.el.setXY(xy);
34312         this.el.show();
34313         this.hidden = false;
34314         this.focus();
34315         this.fireEvent("show", this);
34316     },
34317
34318     focus : function(){
34319         if(!this.hidden){
34320             this.doFocus.defer(50, this);
34321         }
34322     },
34323
34324     doFocus : function(){
34325         if(!this.hidden){
34326             this.focusEl.focus();
34327         }
34328     },
34329
34330     /**
34331      * Hides this menu and optionally all parent menus
34332      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34333      */
34334     hide : function(deep){
34335         if(this.el && this.isVisible()){
34336             this.fireEvent("beforehide", this);
34337             if(this.activeItem){
34338                 this.activeItem.deactivate();
34339                 this.activeItem = null;
34340             }
34341             this.el.hide();
34342             this.hidden = true;
34343             this.fireEvent("hide", this);
34344         }
34345         if(deep === true && this.parentMenu){
34346             this.parentMenu.hide(true);
34347         }
34348     },
34349
34350     /**
34351      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34352      * Any of the following are valid:
34353      * <ul>
34354      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34355      * <li>An HTMLElement object which will be converted to a menu item</li>
34356      * <li>A menu item config object that will be created as a new menu item</li>
34357      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34358      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34359      * </ul>
34360      * Usage:
34361      * <pre><code>
34362 // Create the menu
34363 var menu = new Roo.menu.Menu();
34364
34365 // Create a menu item to add by reference
34366 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34367
34368 // Add a bunch of items at once using different methods.
34369 // Only the last item added will be returned.
34370 var item = menu.add(
34371     menuItem,                // add existing item by ref
34372     'Dynamic Item',          // new TextItem
34373     '-',                     // new separator
34374     { text: 'Config Item' }  // new item by config
34375 );
34376 </code></pre>
34377      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34378      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34379      */
34380     add : function(){
34381         var a = arguments, l = a.length, item;
34382         for(var i = 0; i < l; i++){
34383             var el = a[i];
34384             if ((typeof(el) == "object") && el.xtype && el.xns) {
34385                 el = Roo.factory(el, Roo.menu);
34386             }
34387             
34388             if(el.render){ // some kind of Item
34389                 item = this.addItem(el);
34390             }else if(typeof el == "string"){ // string
34391                 if(el == "separator" || el == "-"){
34392                     item = this.addSeparator();
34393                 }else{
34394                     item = this.addText(el);
34395                 }
34396             }else if(el.tagName || el.el){ // element
34397                 item = this.addElement(el);
34398             }else if(typeof el == "object"){ // must be menu item config?
34399                 item = this.addMenuItem(el);
34400             }
34401         }
34402         return item;
34403     },
34404
34405     /**
34406      * Returns this menu's underlying {@link Roo.Element} object
34407      * @return {Roo.Element} The element
34408      */
34409     getEl : function(){
34410         if(!this.el){
34411             this.render();
34412         }
34413         return this.el;
34414     },
34415
34416     /**
34417      * Adds a separator bar to the menu
34418      * @return {Roo.menu.Item} The menu item that was added
34419      */
34420     addSeparator : function(){
34421         return this.addItem(new Roo.menu.Separator());
34422     },
34423
34424     /**
34425      * Adds an {@link Roo.Element} object to the menu
34426      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34427      * @return {Roo.menu.Item} The menu item that was added
34428      */
34429     addElement : function(el){
34430         return this.addItem(new Roo.menu.BaseItem(el));
34431     },
34432
34433     /**
34434      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34435      * @param {Roo.menu.Item} item The menu item to add
34436      * @return {Roo.menu.Item} The menu item that was added
34437      */
34438     addItem : function(item){
34439         this.items.add(item);
34440         if(this.ul){
34441             var li = document.createElement("li");
34442             li.className = "x-menu-list-item";
34443             this.ul.dom.appendChild(li);
34444             item.render(li, this);
34445             this.delayAutoWidth();
34446         }
34447         return item;
34448     },
34449
34450     /**
34451      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34452      * @param {Object} config A MenuItem config object
34453      * @return {Roo.menu.Item} The menu item that was added
34454      */
34455     addMenuItem : function(config){
34456         if(!(config instanceof Roo.menu.Item)){
34457             if(typeof config.checked == "boolean"){ // must be check menu item config?
34458                 config = new Roo.menu.CheckItem(config);
34459             }else{
34460                 config = new Roo.menu.Item(config);
34461             }
34462         }
34463         return this.addItem(config);
34464     },
34465
34466     /**
34467      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34468      * @param {String} text The text to display in the menu item
34469      * @return {Roo.menu.Item} The menu item that was added
34470      */
34471     addText : function(text){
34472         return this.addItem(new Roo.menu.TextItem({ text : text }));
34473     },
34474
34475     /**
34476      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34477      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34478      * @param {Roo.menu.Item} item The menu item to add
34479      * @return {Roo.menu.Item} The menu item that was added
34480      */
34481     insert : function(index, item){
34482         this.items.insert(index, item);
34483         if(this.ul){
34484             var li = document.createElement("li");
34485             li.className = "x-menu-list-item";
34486             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34487             item.render(li, this);
34488             this.delayAutoWidth();
34489         }
34490         return item;
34491     },
34492
34493     /**
34494      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34495      * @param {Roo.menu.Item} item The menu item to remove
34496      */
34497     remove : function(item){
34498         this.items.removeKey(item.id);
34499         item.destroy();
34500     },
34501
34502     /**
34503      * Removes and destroys all items in the menu
34504      */
34505     removeAll : function(){
34506         var f;
34507         while(f = this.items.first()){
34508             this.remove(f);
34509         }
34510     }
34511 });
34512
34513 // MenuNav is a private utility class used internally by the Menu
34514 Roo.menu.MenuNav = function(menu){
34515     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34516     this.scope = this.menu = menu;
34517 };
34518
34519 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34520     doRelay : function(e, h){
34521         var k = e.getKey();
34522         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34523             this.menu.tryActivate(0, 1);
34524             return false;
34525         }
34526         return h.call(this.scope || this, e, this.menu);
34527     },
34528
34529     up : function(e, m){
34530         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34531             m.tryActivate(m.items.length-1, -1);
34532         }
34533     },
34534
34535     down : function(e, m){
34536         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34537             m.tryActivate(0, 1);
34538         }
34539     },
34540
34541     right : function(e, m){
34542         if(m.activeItem){
34543             m.activeItem.expandMenu(true);
34544         }
34545     },
34546
34547     left : function(e, m){
34548         m.hide();
34549         if(m.parentMenu && m.parentMenu.activeItem){
34550             m.parentMenu.activeItem.activate();
34551         }
34552     },
34553
34554     enter : function(e, m){
34555         if(m.activeItem){
34556             e.stopPropagation();
34557             m.activeItem.onClick(e);
34558             m.fireEvent("click", this, m.activeItem);
34559             return true;
34560         }
34561     }
34562 });/*
34563  * Based on:
34564  * Ext JS Library 1.1.1
34565  * Copyright(c) 2006-2007, Ext JS, LLC.
34566  *
34567  * Originally Released Under LGPL - original licence link has changed is not relivant.
34568  *
34569  * Fork - LGPL
34570  * <script type="text/javascript">
34571  */
34572  
34573 /**
34574  * @class Roo.menu.MenuMgr
34575  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34576  * @singleton
34577  */
34578 Roo.menu.MenuMgr = function(){
34579    var menus, active, groups = {}, attached = false, lastShow = new Date();
34580
34581    // private - called when first menu is created
34582    function init(){
34583        menus = {};
34584        active = new Roo.util.MixedCollection();
34585        Roo.get(document).addKeyListener(27, function(){
34586            if(active.length > 0){
34587                hideAll();
34588            }
34589        });
34590    }
34591
34592    // private
34593    function hideAll(){
34594        if(active && active.length > 0){
34595            var c = active.clone();
34596            c.each(function(m){
34597                m.hide();
34598            });
34599        }
34600    }
34601
34602    // private
34603    function onHide(m){
34604        active.remove(m);
34605        if(active.length < 1){
34606            Roo.get(document).un("mousedown", onMouseDown);
34607            attached = false;
34608        }
34609    }
34610
34611    // private
34612    function onShow(m){
34613        var last = active.last();
34614        lastShow = new Date();
34615        active.add(m);
34616        if(!attached){
34617            Roo.get(document).on("mousedown", onMouseDown);
34618            attached = true;
34619        }
34620        if(m.parentMenu){
34621           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34622           m.parentMenu.activeChild = m;
34623        }else if(last && last.isVisible()){
34624           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34625        }
34626    }
34627
34628    // private
34629    function onBeforeHide(m){
34630        if(m.activeChild){
34631            m.activeChild.hide();
34632        }
34633        if(m.autoHideTimer){
34634            clearTimeout(m.autoHideTimer);
34635            delete m.autoHideTimer;
34636        }
34637    }
34638
34639    // private
34640    function onBeforeShow(m){
34641        var pm = m.parentMenu;
34642        if(!pm && !m.allowOtherMenus){
34643            hideAll();
34644        }else if(pm && pm.activeChild && active != m){
34645            pm.activeChild.hide();
34646        }
34647    }
34648
34649    // private
34650    function onMouseDown(e){
34651        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34652            hideAll();
34653        }
34654    }
34655
34656    // private
34657    function onBeforeCheck(mi, state){
34658        if(state){
34659            var g = groups[mi.group];
34660            for(var i = 0, l = g.length; i < l; i++){
34661                if(g[i] != mi){
34662                    g[i].setChecked(false);
34663                }
34664            }
34665        }
34666    }
34667
34668    return {
34669
34670        /**
34671         * Hides all menus that are currently visible
34672         */
34673        hideAll : function(){
34674             hideAll();  
34675        },
34676
34677        // private
34678        register : function(menu){
34679            if(!menus){
34680                init();
34681            }
34682            menus[menu.id] = menu;
34683            menu.on("beforehide", onBeforeHide);
34684            menu.on("hide", onHide);
34685            menu.on("beforeshow", onBeforeShow);
34686            menu.on("show", onShow);
34687            var g = menu.group;
34688            if(g && menu.events["checkchange"]){
34689                if(!groups[g]){
34690                    groups[g] = [];
34691                }
34692                groups[g].push(menu);
34693                menu.on("checkchange", onCheck);
34694            }
34695        },
34696
34697         /**
34698          * Returns a {@link Roo.menu.Menu} object
34699          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34700          * be used to generate and return a new Menu instance.
34701          */
34702        get : function(menu){
34703            if(typeof menu == "string"){ // menu id
34704                return menus[menu];
34705            }else if(menu.events){  // menu instance
34706                return menu;
34707            }else if(typeof menu.length == 'number'){ // array of menu items?
34708                return new Roo.menu.Menu({items:menu});
34709            }else{ // otherwise, must be a config
34710                return new Roo.menu.Menu(menu);
34711            }
34712        },
34713
34714        // private
34715        unregister : function(menu){
34716            delete menus[menu.id];
34717            menu.un("beforehide", onBeforeHide);
34718            menu.un("hide", onHide);
34719            menu.un("beforeshow", onBeforeShow);
34720            menu.un("show", onShow);
34721            var g = menu.group;
34722            if(g && menu.events["checkchange"]){
34723                groups[g].remove(menu);
34724                menu.un("checkchange", onCheck);
34725            }
34726        },
34727
34728        // private
34729        registerCheckable : function(menuItem){
34730            var g = menuItem.group;
34731            if(g){
34732                if(!groups[g]){
34733                    groups[g] = [];
34734                }
34735                groups[g].push(menuItem);
34736                menuItem.on("beforecheckchange", onBeforeCheck);
34737            }
34738        },
34739
34740        // private
34741        unregisterCheckable : function(menuItem){
34742            var g = menuItem.group;
34743            if(g){
34744                groups[g].remove(menuItem);
34745                menuItem.un("beforecheckchange", onBeforeCheck);
34746            }
34747        }
34748    };
34749 }();/*
34750  * Based on:
34751  * Ext JS Library 1.1.1
34752  * Copyright(c) 2006-2007, Ext JS, LLC.
34753  *
34754  * Originally Released Under LGPL - original licence link has changed is not relivant.
34755  *
34756  * Fork - LGPL
34757  * <script type="text/javascript">
34758  */
34759  
34760
34761 /**
34762  * @class Roo.menu.BaseItem
34763  * @extends Roo.Component
34764  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34765  * management and base configuration options shared by all menu components.
34766  * @constructor
34767  * Creates a new BaseItem
34768  * @param {Object} config Configuration options
34769  */
34770 Roo.menu.BaseItem = function(config){
34771     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34772
34773     this.addEvents({
34774         /**
34775          * @event click
34776          * Fires when this item is clicked
34777          * @param {Roo.menu.BaseItem} this
34778          * @param {Roo.EventObject} e
34779          */
34780         click: true,
34781         /**
34782          * @event activate
34783          * Fires when this item is activated
34784          * @param {Roo.menu.BaseItem} this
34785          */
34786         activate : true,
34787         /**
34788          * @event deactivate
34789          * Fires when this item is deactivated
34790          * @param {Roo.menu.BaseItem} this
34791          */
34792         deactivate : true
34793     });
34794
34795     if(this.handler){
34796         this.on("click", this.handler, this.scope, true);
34797     }
34798 };
34799
34800 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34801     /**
34802      * @cfg {Function} handler
34803      * A function that will handle the click event of this menu item (defaults to undefined)
34804      */
34805     /**
34806      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34807      */
34808     canActivate : false,
34809     /**
34810      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34811      */
34812     activeClass : "x-menu-item-active",
34813     /**
34814      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34815      */
34816     hideOnClick : true,
34817     /**
34818      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34819      */
34820     hideDelay : 100,
34821
34822     // private
34823     ctype: "Roo.menu.BaseItem",
34824
34825     // private
34826     actionMode : "container",
34827
34828     // private
34829     render : function(container, parentMenu){
34830         this.parentMenu = parentMenu;
34831         Roo.menu.BaseItem.superclass.render.call(this, container);
34832         this.container.menuItemId = this.id;
34833     },
34834
34835     // private
34836     onRender : function(container, position){
34837         this.el = Roo.get(this.el);
34838         container.dom.appendChild(this.el.dom);
34839     },
34840
34841     // private
34842     onClick : function(e){
34843         if(!this.disabled && this.fireEvent("click", this, e) !== false
34844                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34845             this.handleClick(e);
34846         }else{
34847             e.stopEvent();
34848         }
34849     },
34850
34851     // private
34852     activate : function(){
34853         if(this.disabled){
34854             return false;
34855         }
34856         var li = this.container;
34857         li.addClass(this.activeClass);
34858         this.region = li.getRegion().adjust(2, 2, -2, -2);
34859         this.fireEvent("activate", this);
34860         return true;
34861     },
34862
34863     // private
34864     deactivate : function(){
34865         this.container.removeClass(this.activeClass);
34866         this.fireEvent("deactivate", this);
34867     },
34868
34869     // private
34870     shouldDeactivate : function(e){
34871         return !this.region || !this.region.contains(e.getPoint());
34872     },
34873
34874     // private
34875     handleClick : function(e){
34876         if(this.hideOnClick){
34877             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34878         }
34879     },
34880
34881     // private
34882     expandMenu : function(autoActivate){
34883         // do nothing
34884     },
34885
34886     // private
34887     hideMenu : function(){
34888         // do nothing
34889     }
34890 });/*
34891  * Based on:
34892  * Ext JS Library 1.1.1
34893  * Copyright(c) 2006-2007, Ext JS, LLC.
34894  *
34895  * Originally Released Under LGPL - original licence link has changed is not relivant.
34896  *
34897  * Fork - LGPL
34898  * <script type="text/javascript">
34899  */
34900  
34901 /**
34902  * @class Roo.menu.Adapter
34903  * @extends Roo.menu.BaseItem
34904  * 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.
34905  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34906  * @constructor
34907  * Creates a new Adapter
34908  * @param {Object} config Configuration options
34909  */
34910 Roo.menu.Adapter = function(component, config){
34911     Roo.menu.Adapter.superclass.constructor.call(this, config);
34912     this.component = component;
34913 };
34914 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34915     // private
34916     canActivate : true,
34917
34918     // private
34919     onRender : function(container, position){
34920         this.component.render(container);
34921         this.el = this.component.getEl();
34922     },
34923
34924     // private
34925     activate : function(){
34926         if(this.disabled){
34927             return false;
34928         }
34929         this.component.focus();
34930         this.fireEvent("activate", this);
34931         return true;
34932     },
34933
34934     // private
34935     deactivate : function(){
34936         this.fireEvent("deactivate", this);
34937     },
34938
34939     // private
34940     disable : function(){
34941         this.component.disable();
34942         Roo.menu.Adapter.superclass.disable.call(this);
34943     },
34944
34945     // private
34946     enable : function(){
34947         this.component.enable();
34948         Roo.menu.Adapter.superclass.enable.call(this);
34949     }
34950 });/*
34951  * Based on:
34952  * Ext JS Library 1.1.1
34953  * Copyright(c) 2006-2007, Ext JS, LLC.
34954  *
34955  * Originally Released Under LGPL - original licence link has changed is not relivant.
34956  *
34957  * Fork - LGPL
34958  * <script type="text/javascript">
34959  */
34960
34961 /**
34962  * @class Roo.menu.TextItem
34963  * @extends Roo.menu.BaseItem
34964  * Adds a static text string to a menu, usually used as either a heading or group separator.
34965  * Note: old style constructor with text is still supported.
34966  * 
34967  * @constructor
34968  * Creates a new TextItem
34969  * @param {Object} cfg Configuration
34970  */
34971 Roo.menu.TextItem = function(cfg){
34972     if (typeof(cfg) == 'string') {
34973         this.text = cfg;
34974     } else {
34975         Roo.apply(this,cfg);
34976     }
34977     
34978     Roo.menu.TextItem.superclass.constructor.call(this);
34979 };
34980
34981 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34982     /**
34983      * @cfg {Boolean} text Text to show on item.
34984      */
34985     text : '',
34986     
34987     /**
34988      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34989      */
34990     hideOnClick : false,
34991     /**
34992      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34993      */
34994     itemCls : "x-menu-text",
34995
34996     // private
34997     onRender : function(){
34998         var s = document.createElement("span");
34999         s.className = this.itemCls;
35000         s.innerHTML = this.text;
35001         this.el = s;
35002         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35003     }
35004 });/*
35005  * Based on:
35006  * Ext JS Library 1.1.1
35007  * Copyright(c) 2006-2007, Ext JS, LLC.
35008  *
35009  * Originally Released Under LGPL - original licence link has changed is not relivant.
35010  *
35011  * Fork - LGPL
35012  * <script type="text/javascript">
35013  */
35014
35015 /**
35016  * @class Roo.menu.Separator
35017  * @extends Roo.menu.BaseItem
35018  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35019  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35020  * @constructor
35021  * @param {Object} config Configuration options
35022  */
35023 Roo.menu.Separator = function(config){
35024     Roo.menu.Separator.superclass.constructor.call(this, config);
35025 };
35026
35027 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35028     /**
35029      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35030      */
35031     itemCls : "x-menu-sep",
35032     /**
35033      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35034      */
35035     hideOnClick : false,
35036
35037     // private
35038     onRender : function(li){
35039         var s = document.createElement("span");
35040         s.className = this.itemCls;
35041         s.innerHTML = "&#160;";
35042         this.el = s;
35043         li.addClass("x-menu-sep-li");
35044         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35045     }
35046 });/*
35047  * Based on:
35048  * Ext JS Library 1.1.1
35049  * Copyright(c) 2006-2007, Ext JS, LLC.
35050  *
35051  * Originally Released Under LGPL - original licence link has changed is not relivant.
35052  *
35053  * Fork - LGPL
35054  * <script type="text/javascript">
35055  */
35056 /**
35057  * @class Roo.menu.Item
35058  * @extends Roo.menu.BaseItem
35059  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35060  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35061  * activation and click handling.
35062  * @constructor
35063  * Creates a new Item
35064  * @param {Object} config Configuration options
35065  */
35066 Roo.menu.Item = function(config){
35067     Roo.menu.Item.superclass.constructor.call(this, config);
35068     if(this.menu){
35069         this.menu = Roo.menu.MenuMgr.get(this.menu);
35070     }
35071 };
35072 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35073     
35074     /**
35075      * @cfg {String} text
35076      * The text to show on the menu item.
35077      */
35078     text: '',
35079      /**
35080      * @cfg {String} HTML to render in menu
35081      * The text to show on the menu item (HTML version).
35082      */
35083     html: '',
35084     /**
35085      * @cfg {String} icon
35086      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35087      */
35088     icon: undefined,
35089     /**
35090      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35091      */
35092     itemCls : "x-menu-item",
35093     /**
35094      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35095      */
35096     canActivate : true,
35097     /**
35098      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35099      */
35100     showDelay: 200,
35101     // doc'd in BaseItem
35102     hideDelay: 200,
35103
35104     // private
35105     ctype: "Roo.menu.Item",
35106     
35107     // private
35108     onRender : function(container, position){
35109         var el = document.createElement("a");
35110         el.hideFocus = true;
35111         el.unselectable = "on";
35112         el.href = this.href || "#";
35113         if(this.hrefTarget){
35114             el.target = this.hrefTarget;
35115         }
35116         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35117         
35118         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35119         
35120         el.innerHTML = String.format(
35121                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35122                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35123         this.el = el;
35124         Roo.menu.Item.superclass.onRender.call(this, container, position);
35125     },
35126
35127     /**
35128      * Sets the text to display in this menu item
35129      * @param {String} text The text to display
35130      * @param {Boolean} isHTML true to indicate text is pure html.
35131      */
35132     setText : function(text, isHTML){
35133         if (isHTML) {
35134             this.html = text;
35135         } else {
35136             this.text = text;
35137             this.html = '';
35138         }
35139         if(this.rendered){
35140             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35141      
35142             this.el.update(String.format(
35143                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35144                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35145             this.parentMenu.autoWidth();
35146         }
35147     },
35148
35149     // private
35150     handleClick : function(e){
35151         if(!this.href){ // if no link defined, stop the event automatically
35152             e.stopEvent();
35153         }
35154         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35155     },
35156
35157     // private
35158     activate : function(autoExpand){
35159         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35160             this.focus();
35161             if(autoExpand){
35162                 this.expandMenu();
35163             }
35164         }
35165         return true;
35166     },
35167
35168     // private
35169     shouldDeactivate : function(e){
35170         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35171             if(this.menu && this.menu.isVisible()){
35172                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35173             }
35174             return true;
35175         }
35176         return false;
35177     },
35178
35179     // private
35180     deactivate : function(){
35181         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35182         this.hideMenu();
35183     },
35184
35185     // private
35186     expandMenu : function(autoActivate){
35187         if(!this.disabled && this.menu){
35188             clearTimeout(this.hideTimer);
35189             delete this.hideTimer;
35190             if(!this.menu.isVisible() && !this.showTimer){
35191                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35192             }else if (this.menu.isVisible() && autoActivate){
35193                 this.menu.tryActivate(0, 1);
35194             }
35195         }
35196     },
35197
35198     // private
35199     deferExpand : function(autoActivate){
35200         delete this.showTimer;
35201         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35202         if(autoActivate){
35203             this.menu.tryActivate(0, 1);
35204         }
35205     },
35206
35207     // private
35208     hideMenu : function(){
35209         clearTimeout(this.showTimer);
35210         delete this.showTimer;
35211         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35212             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35213         }
35214     },
35215
35216     // private
35217     deferHide : function(){
35218         delete this.hideTimer;
35219         this.menu.hide();
35220     }
35221 });/*
35222  * Based on:
35223  * Ext JS Library 1.1.1
35224  * Copyright(c) 2006-2007, Ext JS, LLC.
35225  *
35226  * Originally Released Under LGPL - original licence link has changed is not relivant.
35227  *
35228  * Fork - LGPL
35229  * <script type="text/javascript">
35230  */
35231  
35232 /**
35233  * @class Roo.menu.CheckItem
35234  * @extends Roo.menu.Item
35235  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35236  * @constructor
35237  * Creates a new CheckItem
35238  * @param {Object} config Configuration options
35239  */
35240 Roo.menu.CheckItem = function(config){
35241     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35242     this.addEvents({
35243         /**
35244          * @event beforecheckchange
35245          * Fires before the checked value is set, providing an opportunity to cancel if needed
35246          * @param {Roo.menu.CheckItem} this
35247          * @param {Boolean} checked The new checked value that will be set
35248          */
35249         "beforecheckchange" : true,
35250         /**
35251          * @event checkchange
35252          * Fires after the checked value has been set
35253          * @param {Roo.menu.CheckItem} this
35254          * @param {Boolean} checked The checked value that was set
35255          */
35256         "checkchange" : true
35257     });
35258     if(this.checkHandler){
35259         this.on('checkchange', this.checkHandler, this.scope);
35260     }
35261 };
35262 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35263     /**
35264      * @cfg {String} group
35265      * All check items with the same group name will automatically be grouped into a single-select
35266      * radio button group (defaults to '')
35267      */
35268     /**
35269      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35270      */
35271     itemCls : "x-menu-item x-menu-check-item",
35272     /**
35273      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35274      */
35275     groupClass : "x-menu-group-item",
35276
35277     /**
35278      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35279      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35280      * initialized with checked = true will be rendered as checked.
35281      */
35282     checked: false,
35283
35284     // private
35285     ctype: "Roo.menu.CheckItem",
35286
35287     // private
35288     onRender : function(c){
35289         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35290         if(this.group){
35291             this.el.addClass(this.groupClass);
35292         }
35293         Roo.menu.MenuMgr.registerCheckable(this);
35294         if(this.checked){
35295             this.checked = false;
35296             this.setChecked(true, true);
35297         }
35298     },
35299
35300     // private
35301     destroy : function(){
35302         if(this.rendered){
35303             Roo.menu.MenuMgr.unregisterCheckable(this);
35304         }
35305         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35306     },
35307
35308     /**
35309      * Set the checked state of this item
35310      * @param {Boolean} checked The new checked value
35311      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35312      */
35313     setChecked : function(state, suppressEvent){
35314         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35315             if(this.container){
35316                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35317             }
35318             this.checked = state;
35319             if(suppressEvent !== true){
35320                 this.fireEvent("checkchange", this, state);
35321             }
35322         }
35323     },
35324
35325     // private
35326     handleClick : function(e){
35327        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35328            this.setChecked(!this.checked);
35329        }
35330        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35331     }
35332 });/*
35333  * Based on:
35334  * Ext JS Library 1.1.1
35335  * Copyright(c) 2006-2007, Ext JS, LLC.
35336  *
35337  * Originally Released Under LGPL - original licence link has changed is not relivant.
35338  *
35339  * Fork - LGPL
35340  * <script type="text/javascript">
35341  */
35342  
35343 /**
35344  * @class Roo.menu.DateItem
35345  * @extends Roo.menu.Adapter
35346  * A menu item that wraps the {@link Roo.DatPicker} component.
35347  * @constructor
35348  * Creates a new DateItem
35349  * @param {Object} config Configuration options
35350  */
35351 Roo.menu.DateItem = function(config){
35352     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35353     /** The Roo.DatePicker object @type Roo.DatePicker */
35354     this.picker = this.component;
35355     this.addEvents({select: true});
35356     
35357     this.picker.on("render", function(picker){
35358         picker.getEl().swallowEvent("click");
35359         picker.container.addClass("x-menu-date-item");
35360     });
35361
35362     this.picker.on("select", this.onSelect, this);
35363 };
35364
35365 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35366     // private
35367     onSelect : function(picker, date){
35368         this.fireEvent("select", this, date, picker);
35369         Roo.menu.DateItem.superclass.handleClick.call(this);
35370     }
35371 });/*
35372  * Based on:
35373  * Ext JS Library 1.1.1
35374  * Copyright(c) 2006-2007, Ext JS, LLC.
35375  *
35376  * Originally Released Under LGPL - original licence link has changed is not relivant.
35377  *
35378  * Fork - LGPL
35379  * <script type="text/javascript">
35380  */
35381  
35382 /**
35383  * @class Roo.menu.ColorItem
35384  * @extends Roo.menu.Adapter
35385  * A menu item that wraps the {@link Roo.ColorPalette} component.
35386  * @constructor
35387  * Creates a new ColorItem
35388  * @param {Object} config Configuration options
35389  */
35390 Roo.menu.ColorItem = function(config){
35391     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35392     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35393     this.palette = this.component;
35394     this.relayEvents(this.palette, ["select"]);
35395     if(this.selectHandler){
35396         this.on('select', this.selectHandler, this.scope);
35397     }
35398 };
35399 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35400  * Based on:
35401  * Ext JS Library 1.1.1
35402  * Copyright(c) 2006-2007, Ext JS, LLC.
35403  *
35404  * Originally Released Under LGPL - original licence link has changed is not relivant.
35405  *
35406  * Fork - LGPL
35407  * <script type="text/javascript">
35408  */
35409  
35410
35411 /**
35412  * @class Roo.menu.DateMenu
35413  * @extends Roo.menu.Menu
35414  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35415  * @constructor
35416  * Creates a new DateMenu
35417  * @param {Object} config Configuration options
35418  */
35419 Roo.menu.DateMenu = function(config){
35420     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35421     this.plain = true;
35422     var di = new Roo.menu.DateItem(config);
35423     this.add(di);
35424     /**
35425      * The {@link Roo.DatePicker} instance for this DateMenu
35426      * @type DatePicker
35427      */
35428     this.picker = di.picker;
35429     /**
35430      * @event select
35431      * @param {DatePicker} picker
35432      * @param {Date} date
35433      */
35434     this.relayEvents(di, ["select"]);
35435
35436     this.on('beforeshow', function(){
35437         if(this.picker){
35438             this.picker.hideMonthPicker(true);
35439         }
35440     }, this);
35441 };
35442 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35443     cls:'x-date-menu'
35444 });/*
35445  * Based on:
35446  * Ext JS Library 1.1.1
35447  * Copyright(c) 2006-2007, Ext JS, LLC.
35448  *
35449  * Originally Released Under LGPL - original licence link has changed is not relivant.
35450  *
35451  * Fork - LGPL
35452  * <script type="text/javascript">
35453  */
35454  
35455
35456 /**
35457  * @class Roo.menu.ColorMenu
35458  * @extends Roo.menu.Menu
35459  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35460  * @constructor
35461  * Creates a new ColorMenu
35462  * @param {Object} config Configuration options
35463  */
35464 Roo.menu.ColorMenu = function(config){
35465     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35466     this.plain = true;
35467     var ci = new Roo.menu.ColorItem(config);
35468     this.add(ci);
35469     /**
35470      * The {@link Roo.ColorPalette} instance for this ColorMenu
35471      * @type ColorPalette
35472      */
35473     this.palette = ci.palette;
35474     /**
35475      * @event select
35476      * @param {ColorPalette} palette
35477      * @param {String} color
35478      */
35479     this.relayEvents(ci, ["select"]);
35480 };
35481 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35482  * Based on:
35483  * Ext JS Library 1.1.1
35484  * Copyright(c) 2006-2007, Ext JS, LLC.
35485  *
35486  * Originally Released Under LGPL - original licence link has changed is not relivant.
35487  *
35488  * Fork - LGPL
35489  * <script type="text/javascript">
35490  */
35491  
35492 /**
35493  * @class Roo.form.Field
35494  * @extends Roo.BoxComponent
35495  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35496  * @constructor
35497  * Creates a new Field
35498  * @param {Object} config Configuration options
35499  */
35500 Roo.form.Field = function(config){
35501     Roo.form.Field.superclass.constructor.call(this, config);
35502 };
35503
35504 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35505     /**
35506      * @cfg {String} fieldLabel Label to use when rendering a form.
35507      */
35508        /**
35509      * @cfg {String} qtip Mouse over tip
35510      */
35511      
35512     /**
35513      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35514      */
35515     invalidClass : "x-form-invalid",
35516     /**
35517      * @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")
35518      */
35519     invalidText : "The value in this field is invalid",
35520     /**
35521      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35522      */
35523     focusClass : "x-form-focus",
35524     /**
35525      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35526       automatic validation (defaults to "keyup").
35527      */
35528     validationEvent : "keyup",
35529     /**
35530      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35531      */
35532     validateOnBlur : true,
35533     /**
35534      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35535      */
35536     validationDelay : 250,
35537     /**
35538      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35539      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35540      */
35541     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35542     /**
35543      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35544      */
35545     fieldClass : "x-form-field",
35546     /**
35547      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35548      *<pre>
35549 Value         Description
35550 -----------   ----------------------------------------------------------------------
35551 qtip          Display a quick tip when the user hovers over the field
35552 title         Display a default browser title attribute popup
35553 under         Add a block div beneath the field containing the error text
35554 side          Add an error icon to the right of the field with a popup on hover
35555 [element id]  Add the error text directly to the innerHTML of the specified element
35556 </pre>
35557      */
35558     msgTarget : 'qtip',
35559     /**
35560      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35561      */
35562     msgFx : 'normal',
35563
35564     /**
35565      * @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.
35566      */
35567     readOnly : false,
35568
35569     /**
35570      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35571      */
35572     disabled : false,
35573
35574     /**
35575      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35576      */
35577     inputType : undefined,
35578     
35579     /**
35580      * @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).
35581          */
35582         tabIndex : undefined,
35583         
35584     // private
35585     isFormField : true,
35586
35587     // private
35588     hasFocus : false,
35589     /**
35590      * @property {Roo.Element} fieldEl
35591      * Element Containing the rendered Field (with label etc.)
35592      */
35593     /**
35594      * @cfg {Mixed} value A value to initialize this field with.
35595      */
35596     value : undefined,
35597
35598     /**
35599      * @cfg {String} name The field's HTML name attribute.
35600      */
35601     /**
35602      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35603      */
35604
35605         // private ??
35606         initComponent : function(){
35607         Roo.form.Field.superclass.initComponent.call(this);
35608         this.addEvents({
35609             /**
35610              * @event focus
35611              * Fires when this field receives input focus.
35612              * @param {Roo.form.Field} this
35613              */
35614             focus : true,
35615             /**
35616              * @event blur
35617              * Fires when this field loses input focus.
35618              * @param {Roo.form.Field} this
35619              */
35620             blur : true,
35621             /**
35622              * @event specialkey
35623              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35624              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35625              * @param {Roo.form.Field} this
35626              * @param {Roo.EventObject} e The event object
35627              */
35628             specialkey : true,
35629             /**
35630              * @event change
35631              * Fires just before the field blurs if the field value has changed.
35632              * @param {Roo.form.Field} this
35633              * @param {Mixed} newValue The new value
35634              * @param {Mixed} oldValue The original value
35635              */
35636             change : true,
35637             /**
35638              * @event invalid
35639              * Fires after the field has been marked as invalid.
35640              * @param {Roo.form.Field} this
35641              * @param {String} msg The validation message
35642              */
35643             invalid : true,
35644             /**
35645              * @event valid
35646              * Fires after the field has been validated with no errors.
35647              * @param {Roo.form.Field} this
35648              */
35649             valid : true,
35650              /**
35651              * @event keyup
35652              * Fires after the key up
35653              * @param {Roo.form.Field} this
35654              * @param {Roo.EventObject}  e The event Object
35655              */
35656             keyup : true
35657         });
35658     },
35659
35660     /**
35661      * Returns the name attribute of the field if available
35662      * @return {String} name The field name
35663      */
35664     getName: function(){
35665          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35666     },
35667
35668     // private
35669     onRender : function(ct, position){
35670         Roo.form.Field.superclass.onRender.call(this, ct, position);
35671         if(!this.el){
35672             var cfg = this.getAutoCreate();
35673             if(!cfg.name){
35674                 cfg.name = this.name || this.id;
35675             }
35676             if(this.inputType){
35677                 cfg.type = this.inputType;
35678             }
35679             this.el = ct.createChild(cfg, position);
35680         }
35681         var type = this.el.dom.type;
35682         if(type){
35683             if(type == 'password'){
35684                 type = 'text';
35685             }
35686             this.el.addClass('x-form-'+type);
35687         }
35688         if(this.readOnly){
35689             this.el.dom.readOnly = true;
35690         }
35691         if(this.tabIndex !== undefined){
35692             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35693         }
35694
35695         this.el.addClass([this.fieldClass, this.cls]);
35696         this.initValue();
35697     },
35698
35699     /**
35700      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35701      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35702      * @return {Roo.form.Field} this
35703      */
35704     applyTo : function(target){
35705         this.allowDomMove = false;
35706         this.el = Roo.get(target);
35707         this.render(this.el.dom.parentNode);
35708         return this;
35709     },
35710
35711     // private
35712     initValue : function(){
35713         if(this.value !== undefined){
35714             this.setValue(this.value);
35715         }else if(this.el.dom.value.length > 0){
35716             this.setValue(this.el.dom.value);
35717         }
35718     },
35719
35720     /**
35721      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35722      */
35723     isDirty : function() {
35724         if(this.disabled) {
35725             return false;
35726         }
35727         return String(this.getValue()) !== String(this.originalValue);
35728     },
35729
35730     // private
35731     afterRender : function(){
35732         Roo.form.Field.superclass.afterRender.call(this);
35733         this.initEvents();
35734     },
35735
35736     // private
35737     fireKey : function(e){
35738         //Roo.log('field ' + e.getKey());
35739         if(e.isNavKeyPress()){
35740             this.fireEvent("specialkey", this, e);
35741         }
35742     },
35743
35744     /**
35745      * Resets the current field value to the originally loaded value and clears any validation messages
35746      */
35747     reset : function(){
35748         this.setValue(this.originalValue);
35749         this.clearInvalid();
35750     },
35751
35752     // private
35753     initEvents : function(){
35754         // safari killled keypress - so keydown is now used..
35755         this.el.on("keydown" , this.fireKey,  this);
35756         this.el.on("focus", this.onFocus,  this);
35757         this.el.on("blur", this.onBlur,  this);
35758         this.el.relayEvent('keyup', this);
35759
35760         // reference to original value for reset
35761         this.originalValue = this.getValue();
35762     },
35763
35764     // private
35765     onFocus : function(){
35766         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35767             this.el.addClass(this.focusClass);
35768         }
35769         if(!this.hasFocus){
35770             this.hasFocus = true;
35771             this.startValue = this.getValue();
35772             this.fireEvent("focus", this);
35773         }
35774     },
35775
35776     beforeBlur : Roo.emptyFn,
35777
35778     // private
35779     onBlur : function(){
35780         this.beforeBlur();
35781         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35782             this.el.removeClass(this.focusClass);
35783         }
35784         this.hasFocus = false;
35785         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35786             this.validate();
35787         }
35788         var v = this.getValue();
35789         if(String(v) !== String(this.startValue)){
35790             this.fireEvent('change', this, v, this.startValue);
35791         }
35792         this.fireEvent("blur", this);
35793     },
35794
35795     /**
35796      * Returns whether or not the field value is currently valid
35797      * @param {Boolean} preventMark True to disable marking the field invalid
35798      * @return {Boolean} True if the value is valid, else false
35799      */
35800     isValid : function(preventMark){
35801         if(this.disabled){
35802             return true;
35803         }
35804         var restore = this.preventMark;
35805         this.preventMark = preventMark === true;
35806         var v = this.validateValue(this.processValue(this.getRawValue()));
35807         this.preventMark = restore;
35808         return v;
35809     },
35810
35811     /**
35812      * Validates the field value
35813      * @return {Boolean} True if the value is valid, else false
35814      */
35815     validate : function(){
35816         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35817             this.clearInvalid();
35818             return true;
35819         }
35820         return false;
35821     },
35822
35823     processValue : function(value){
35824         return value;
35825     },
35826
35827     // private
35828     // Subclasses should provide the validation implementation by overriding this
35829     validateValue : function(value){
35830         return true;
35831     },
35832
35833     /**
35834      * Mark this field as invalid
35835      * @param {String} msg The validation message
35836      */
35837     markInvalid : function(msg){
35838         if(!this.rendered || this.preventMark){ // not rendered
35839             return;
35840         }
35841         this.el.addClass(this.invalidClass);
35842         msg = msg || this.invalidText;
35843         switch(this.msgTarget){
35844             case 'qtip':
35845                 this.el.dom.qtip = msg;
35846                 this.el.dom.qclass = 'x-form-invalid-tip';
35847                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35848                     Roo.QuickTips.enable();
35849                 }
35850                 break;
35851             case 'title':
35852                 this.el.dom.title = msg;
35853                 break;
35854             case 'under':
35855                 if(!this.errorEl){
35856                     var elp = this.el.findParent('.x-form-element', 5, true);
35857                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35858                     this.errorEl.setWidth(elp.getWidth(true)-20);
35859                 }
35860                 this.errorEl.update(msg);
35861                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35862                 break;
35863             case 'side':
35864                 if(!this.errorIcon){
35865                     var elp = this.el.findParent('.x-form-element', 5, true);
35866                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35867                 }
35868                 this.alignErrorIcon();
35869                 this.errorIcon.dom.qtip = msg;
35870                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35871                 this.errorIcon.show();
35872                 this.on('resize', this.alignErrorIcon, this);
35873                 break;
35874             default:
35875                 var t = Roo.getDom(this.msgTarget);
35876                 t.innerHTML = msg;
35877                 t.style.display = this.msgDisplay;
35878                 break;
35879         }
35880         this.fireEvent('invalid', this, msg);
35881     },
35882
35883     // private
35884     alignErrorIcon : function(){
35885         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35886     },
35887
35888     /**
35889      * Clear any invalid styles/messages for this field
35890      */
35891     clearInvalid : function(){
35892         if(!this.rendered || this.preventMark){ // not rendered
35893             return;
35894         }
35895         this.el.removeClass(this.invalidClass);
35896         switch(this.msgTarget){
35897             case 'qtip':
35898                 this.el.dom.qtip = '';
35899                 break;
35900             case 'title':
35901                 this.el.dom.title = '';
35902                 break;
35903             case 'under':
35904                 if(this.errorEl){
35905                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35906                 }
35907                 break;
35908             case 'side':
35909                 if(this.errorIcon){
35910                     this.errorIcon.dom.qtip = '';
35911                     this.errorIcon.hide();
35912                     this.un('resize', this.alignErrorIcon, this);
35913                 }
35914                 break;
35915             default:
35916                 var t = Roo.getDom(this.msgTarget);
35917                 t.innerHTML = '';
35918                 t.style.display = 'none';
35919                 break;
35920         }
35921         this.fireEvent('valid', this);
35922     },
35923
35924     /**
35925      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35926      * @return {Mixed} value The field value
35927      */
35928     getRawValue : function(){
35929         var v = this.el.getValue();
35930         if(v === this.emptyText){
35931             v = '';
35932         }
35933         return v;
35934     },
35935
35936     /**
35937      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35938      * @return {Mixed} value The field value
35939      */
35940     getValue : function(){
35941         var v = this.el.getValue();
35942         if(v === this.emptyText || v === undefined){
35943             v = '';
35944         }
35945         return v;
35946     },
35947
35948     /**
35949      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35950      * @param {Mixed} value The value to set
35951      */
35952     setRawValue : function(v){
35953         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35954     },
35955
35956     /**
35957      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35958      * @param {Mixed} value The value to set
35959      */
35960     setValue : function(v){
35961         this.value = v;
35962         if(this.rendered){
35963             this.el.dom.value = (v === null || v === undefined ? '' : v);
35964              this.validate();
35965         }
35966     },
35967
35968     adjustSize : function(w, h){
35969         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35970         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35971         return s;
35972     },
35973
35974     adjustWidth : function(tag, w){
35975         tag = tag.toLowerCase();
35976         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35977             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35978                 if(tag == 'input'){
35979                     return w + 2;
35980                 }
35981                 if(tag = 'textarea'){
35982                     return w-2;
35983                 }
35984             }else if(Roo.isOpera){
35985                 if(tag == 'input'){
35986                     return w + 2;
35987                 }
35988                 if(tag = 'textarea'){
35989                     return w-2;
35990                 }
35991             }
35992         }
35993         return w;
35994     }
35995 });
35996
35997
35998 // anything other than normal should be considered experimental
35999 Roo.form.Field.msgFx = {
36000     normal : {
36001         show: function(msgEl, f){
36002             msgEl.setDisplayed('block');
36003         },
36004
36005         hide : function(msgEl, f){
36006             msgEl.setDisplayed(false).update('');
36007         }
36008     },
36009
36010     slide : {
36011         show: function(msgEl, f){
36012             msgEl.slideIn('t', {stopFx:true});
36013         },
36014
36015         hide : function(msgEl, f){
36016             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36017         }
36018     },
36019
36020     slideRight : {
36021         show: function(msgEl, f){
36022             msgEl.fixDisplay();
36023             msgEl.alignTo(f.el, 'tl-tr');
36024             msgEl.slideIn('l', {stopFx:true});
36025         },
36026
36027         hide : function(msgEl, f){
36028             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36029         }
36030     }
36031 };/*
36032  * Based on:
36033  * Ext JS Library 1.1.1
36034  * Copyright(c) 2006-2007, Ext JS, LLC.
36035  *
36036  * Originally Released Under LGPL - original licence link has changed is not relivant.
36037  *
36038  * Fork - LGPL
36039  * <script type="text/javascript">
36040  */
36041  
36042
36043 /**
36044  * @class Roo.form.TextField
36045  * @extends Roo.form.Field
36046  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36047  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36048  * @constructor
36049  * Creates a new TextField
36050  * @param {Object} config Configuration options
36051  */
36052 Roo.form.TextField = function(config){
36053     Roo.form.TextField.superclass.constructor.call(this, config);
36054     this.addEvents({
36055         /**
36056          * @event autosize
36057          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36058          * according to the default logic, but this event provides a hook for the developer to apply additional
36059          * logic at runtime to resize the field if needed.
36060              * @param {Roo.form.Field} this This text field
36061              * @param {Number} width The new field width
36062              */
36063         autosize : true
36064     });
36065 };
36066
36067 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36068     /**
36069      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36070      */
36071     grow : false,
36072     /**
36073      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36074      */
36075     growMin : 30,
36076     /**
36077      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36078      */
36079     growMax : 800,
36080     /**
36081      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36082      */
36083     vtype : null,
36084     /**
36085      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36086      */
36087     maskRe : null,
36088     /**
36089      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36090      */
36091     disableKeyFilter : false,
36092     /**
36093      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36094      */
36095     allowBlank : true,
36096     /**
36097      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36098      */
36099     minLength : 0,
36100     /**
36101      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36102      */
36103     maxLength : Number.MAX_VALUE,
36104     /**
36105      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36106      */
36107     minLengthText : "The minimum length for this field is {0}",
36108     /**
36109      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36110      */
36111     maxLengthText : "The maximum length for this field is {0}",
36112     /**
36113      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36114      */
36115     selectOnFocus : false,
36116     /**
36117      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36118      */
36119     blankText : "This field is required",
36120     /**
36121      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36122      * If available, this function will be called only after the basic validators all return true, and will be passed the
36123      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36124      */
36125     validator : null,
36126     /**
36127      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36128      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36129      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36130      */
36131     regex : null,
36132     /**
36133      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36134      */
36135     regexText : "",
36136     /**
36137      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36138      */
36139     emptyText : null,
36140     /**
36141      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36142      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36143      */
36144     emptyClass : 'x-form-empty-field',
36145
36146     // private
36147     initEvents : function(){
36148         Roo.form.TextField.superclass.initEvents.call(this);
36149         if(this.validationEvent == 'keyup'){
36150             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36151             this.el.on('keyup', this.filterValidation, this);
36152         }
36153         else if(this.validationEvent !== false){
36154             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36155         }
36156         if(this.selectOnFocus || this.emptyText){
36157             this.on("focus", this.preFocus, this);
36158             if(this.emptyText){
36159                 this.on('blur', this.postBlur, this);
36160                 this.applyEmptyText();
36161             }
36162         }
36163         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36164             this.el.on("keypress", this.filterKeys, this);
36165         }
36166         if(this.grow){
36167             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36168             this.el.on("click", this.autoSize,  this);
36169         }
36170     },
36171
36172     processValue : function(value){
36173         if(this.stripCharsRe){
36174             var newValue = value.replace(this.stripCharsRe, '');
36175             if(newValue !== value){
36176                 this.setRawValue(newValue);
36177                 return newValue;
36178             }
36179         }
36180         return value;
36181     },
36182
36183     filterValidation : function(e){
36184         if(!e.isNavKeyPress()){
36185             this.validationTask.delay(this.validationDelay);
36186         }
36187     },
36188
36189     // private
36190     onKeyUp : function(e){
36191         if(!e.isNavKeyPress()){
36192             this.autoSize();
36193         }
36194     },
36195
36196     /**
36197      * Resets the current field value to the originally-loaded value and clears any validation messages.
36198      * Also adds emptyText and emptyClass if the original value was blank.
36199      */
36200     reset : function(){
36201         Roo.form.TextField.superclass.reset.call(this);
36202         this.applyEmptyText();
36203     },
36204
36205     applyEmptyText : function(){
36206         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36207             this.setRawValue(this.emptyText);
36208             this.el.addClass(this.emptyClass);
36209         }
36210     },
36211
36212     // private
36213     preFocus : function(){
36214         if(this.emptyText){
36215             if(this.el.dom.value == this.emptyText){
36216                 this.setRawValue('');
36217             }
36218             this.el.removeClass(this.emptyClass);
36219         }
36220         if(this.selectOnFocus){
36221             this.el.dom.select();
36222         }
36223     },
36224
36225     // private
36226     postBlur : function(){
36227         this.applyEmptyText();
36228     },
36229
36230     // private
36231     filterKeys : function(e){
36232         var k = e.getKey();
36233         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36234             return;
36235         }
36236         var c = e.getCharCode(), cc = String.fromCharCode(c);
36237         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36238             return;
36239         }
36240         if(!this.maskRe.test(cc)){
36241             e.stopEvent();
36242         }
36243     },
36244
36245     setValue : function(v){
36246         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36247             this.el.removeClass(this.emptyClass);
36248         }
36249         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36250         this.applyEmptyText();
36251         this.autoSize();
36252     },
36253
36254     /**
36255      * Validates a value according to the field's validation rules and marks the field as invalid
36256      * if the validation fails
36257      * @param {Mixed} value The value to validate
36258      * @return {Boolean} True if the value is valid, else false
36259      */
36260     validateValue : function(value){
36261         if(value.length < 1 || value === this.emptyText){ // if it's blank
36262              if(this.allowBlank){
36263                 this.clearInvalid();
36264                 return true;
36265              }else{
36266                 this.markInvalid(this.blankText);
36267                 return false;
36268              }
36269         }
36270         if(value.length < this.minLength){
36271             this.markInvalid(String.format(this.minLengthText, this.minLength));
36272             return false;
36273         }
36274         if(value.length > this.maxLength){
36275             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36276             return false;
36277         }
36278         if(this.vtype){
36279             var vt = Roo.form.VTypes;
36280             if(!vt[this.vtype](value, this)){
36281                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36282                 return false;
36283             }
36284         }
36285         if(typeof this.validator == "function"){
36286             var msg = this.validator(value);
36287             if(msg !== true){
36288                 this.markInvalid(msg);
36289                 return false;
36290             }
36291         }
36292         if(this.regex && !this.regex.test(value)){
36293             this.markInvalid(this.regexText);
36294             return false;
36295         }
36296         return true;
36297     },
36298
36299     /**
36300      * Selects text in this field
36301      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36302      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36303      */
36304     selectText : function(start, end){
36305         var v = this.getRawValue();
36306         if(v.length > 0){
36307             start = start === undefined ? 0 : start;
36308             end = end === undefined ? v.length : end;
36309             var d = this.el.dom;
36310             if(d.setSelectionRange){
36311                 d.setSelectionRange(start, end);
36312             }else if(d.createTextRange){
36313                 var range = d.createTextRange();
36314                 range.moveStart("character", start);
36315                 range.moveEnd("character", v.length-end);
36316                 range.select();
36317             }
36318         }
36319     },
36320
36321     /**
36322      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36323      * This only takes effect if grow = true, and fires the autosize event.
36324      */
36325     autoSize : function(){
36326         if(!this.grow || !this.rendered){
36327             return;
36328         }
36329         if(!this.metrics){
36330             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36331         }
36332         var el = this.el;
36333         var v = el.dom.value;
36334         var d = document.createElement('div');
36335         d.appendChild(document.createTextNode(v));
36336         v = d.innerHTML;
36337         d = null;
36338         v += "&#160;";
36339         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36340         this.el.setWidth(w);
36341         this.fireEvent("autosize", this, w);
36342     }
36343 });/*
36344  * Based on:
36345  * Ext JS Library 1.1.1
36346  * Copyright(c) 2006-2007, Ext JS, LLC.
36347  *
36348  * Originally Released Under LGPL - original licence link has changed is not relivant.
36349  *
36350  * Fork - LGPL
36351  * <script type="text/javascript">
36352  */
36353  
36354 /**
36355  * @class Roo.form.Hidden
36356  * @extends Roo.form.TextField
36357  * Simple Hidden element used on forms 
36358  * 
36359  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36360  * 
36361  * @constructor
36362  * Creates a new Hidden form element.
36363  * @param {Object} config Configuration options
36364  */
36365
36366
36367
36368 // easy hidden field...
36369 Roo.form.Hidden = function(config){
36370     Roo.form.Hidden.superclass.constructor.call(this, config);
36371 };
36372   
36373 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36374     fieldLabel:      '',
36375     inputType:      'hidden',
36376     width:          50,
36377     allowBlank:     true,
36378     labelSeparator: '',
36379     hidden:         true,
36380     itemCls :       'x-form-item-display-none'
36381
36382
36383 });
36384
36385
36386 /*
36387  * Based on:
36388  * Ext JS Library 1.1.1
36389  * Copyright(c) 2006-2007, Ext JS, LLC.
36390  *
36391  * Originally Released Under LGPL - original licence link has changed is not relivant.
36392  *
36393  * Fork - LGPL
36394  * <script type="text/javascript">
36395  */
36396  
36397 /**
36398  * @class Roo.form.TriggerField
36399  * @extends Roo.form.TextField
36400  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36401  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36402  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36403  * for which you can provide a custom implementation.  For example:
36404  * <pre><code>
36405 var trigger = new Roo.form.TriggerField();
36406 trigger.onTriggerClick = myTriggerFn;
36407 trigger.applyTo('my-field');
36408 </code></pre>
36409  *
36410  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36411  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36412  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36413  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36414  * @constructor
36415  * Create a new TriggerField.
36416  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36417  * to the base TextField)
36418  */
36419 Roo.form.TriggerField = function(config){
36420     this.mimicing = false;
36421     Roo.form.TriggerField.superclass.constructor.call(this, config);
36422 };
36423
36424 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36425     /**
36426      * @cfg {String} triggerClass A CSS class to apply to the trigger
36427      */
36428     /**
36429      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36430      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36431      */
36432     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36433     /**
36434      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36435      */
36436     hideTrigger:false,
36437
36438     /** @cfg {Boolean} grow @hide */
36439     /** @cfg {Number} growMin @hide */
36440     /** @cfg {Number} growMax @hide */
36441
36442     /**
36443      * @hide 
36444      * @method
36445      */
36446     autoSize: Roo.emptyFn,
36447     // private
36448     monitorTab : true,
36449     // private
36450     deferHeight : true,
36451
36452     
36453     actionMode : 'wrap',
36454     // private
36455     onResize : function(w, h){
36456         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36457         if(typeof w == 'number'){
36458             var x = w - this.trigger.getWidth();
36459             this.el.setWidth(this.adjustWidth('input', x));
36460             this.trigger.setStyle('left', x+'px');
36461         }
36462     },
36463
36464     // private
36465     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36466
36467     // private
36468     getResizeEl : function(){
36469         return this.wrap;
36470     },
36471
36472     // private
36473     getPositionEl : function(){
36474         return this.wrap;
36475     },
36476
36477     // private
36478     alignErrorIcon : function(){
36479         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36480     },
36481
36482     // private
36483     onRender : function(ct, position){
36484         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36485         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36486         this.trigger = this.wrap.createChild(this.triggerConfig ||
36487                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36488         if(this.hideTrigger){
36489             this.trigger.setDisplayed(false);
36490         }
36491         this.initTrigger();
36492         if(!this.width){
36493             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36494         }
36495     },
36496
36497     // private
36498     initTrigger : function(){
36499         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36500         this.trigger.addClassOnOver('x-form-trigger-over');
36501         this.trigger.addClassOnClick('x-form-trigger-click');
36502     },
36503
36504     // private
36505     onDestroy : function(){
36506         if(this.trigger){
36507             this.trigger.removeAllListeners();
36508             this.trigger.remove();
36509         }
36510         if(this.wrap){
36511             this.wrap.remove();
36512         }
36513         Roo.form.TriggerField.superclass.onDestroy.call(this);
36514     },
36515
36516     // private
36517     onFocus : function(){
36518         Roo.form.TriggerField.superclass.onFocus.call(this);
36519         if(!this.mimicing){
36520             this.wrap.addClass('x-trigger-wrap-focus');
36521             this.mimicing = true;
36522             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36523             if(this.monitorTab){
36524                 this.el.on("keydown", this.checkTab, this);
36525             }
36526         }
36527     },
36528
36529     // private
36530     checkTab : function(e){
36531         if(e.getKey() == e.TAB){
36532             this.triggerBlur();
36533         }
36534     },
36535
36536     // private
36537     onBlur : function(){
36538         // do nothing
36539     },
36540
36541     // private
36542     mimicBlur : function(e, t){
36543         if(!this.wrap.contains(t) && this.validateBlur()){
36544             this.triggerBlur();
36545         }
36546     },
36547
36548     // private
36549     triggerBlur : function(){
36550         this.mimicing = false;
36551         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36552         if(this.monitorTab){
36553             this.el.un("keydown", this.checkTab, this);
36554         }
36555         this.wrap.removeClass('x-trigger-wrap-focus');
36556         Roo.form.TriggerField.superclass.onBlur.call(this);
36557     },
36558
36559     // private
36560     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36561     validateBlur : function(e, t){
36562         return true;
36563     },
36564
36565     // private
36566     onDisable : function(){
36567         Roo.form.TriggerField.superclass.onDisable.call(this);
36568         if(this.wrap){
36569             this.wrap.addClass('x-item-disabled');
36570         }
36571     },
36572
36573     // private
36574     onEnable : function(){
36575         Roo.form.TriggerField.superclass.onEnable.call(this);
36576         if(this.wrap){
36577             this.wrap.removeClass('x-item-disabled');
36578         }
36579     },
36580
36581     // private
36582     onShow : function(){
36583         var ae = this.getActionEl();
36584         
36585         if(ae){
36586             ae.dom.style.display = '';
36587             ae.dom.style.visibility = 'visible';
36588         }
36589     },
36590
36591     // private
36592     
36593     onHide : function(){
36594         var ae = this.getActionEl();
36595         ae.dom.style.display = 'none';
36596     },
36597
36598     /**
36599      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36600      * by an implementing function.
36601      * @method
36602      * @param {EventObject} e
36603      */
36604     onTriggerClick : Roo.emptyFn
36605 });
36606
36607 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36608 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36609 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36610 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36611     initComponent : function(){
36612         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36613
36614         this.triggerConfig = {
36615             tag:'span', cls:'x-form-twin-triggers', cn:[
36616             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36617             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36618         ]};
36619     },
36620
36621     getTrigger : function(index){
36622         return this.triggers[index];
36623     },
36624
36625     initTrigger : function(){
36626         var ts = this.trigger.select('.x-form-trigger', true);
36627         this.wrap.setStyle('overflow', 'hidden');
36628         var triggerField = this;
36629         ts.each(function(t, all, index){
36630             t.hide = function(){
36631                 var w = triggerField.wrap.getWidth();
36632                 this.dom.style.display = 'none';
36633                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36634             };
36635             t.show = function(){
36636                 var w = triggerField.wrap.getWidth();
36637                 this.dom.style.display = '';
36638                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36639             };
36640             var triggerIndex = 'Trigger'+(index+1);
36641
36642             if(this['hide'+triggerIndex]){
36643                 t.dom.style.display = 'none';
36644             }
36645             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36646             t.addClassOnOver('x-form-trigger-over');
36647             t.addClassOnClick('x-form-trigger-click');
36648         }, this);
36649         this.triggers = ts.elements;
36650     },
36651
36652     onTrigger1Click : Roo.emptyFn,
36653     onTrigger2Click : Roo.emptyFn
36654 });/*
36655  * Based on:
36656  * Ext JS Library 1.1.1
36657  * Copyright(c) 2006-2007, Ext JS, LLC.
36658  *
36659  * Originally Released Under LGPL - original licence link has changed is not relivant.
36660  *
36661  * Fork - LGPL
36662  * <script type="text/javascript">
36663  */
36664  
36665 /**
36666  * @class Roo.form.TextArea
36667  * @extends Roo.form.TextField
36668  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36669  * support for auto-sizing.
36670  * @constructor
36671  * Creates a new TextArea
36672  * @param {Object} config Configuration options
36673  */
36674 Roo.form.TextArea = function(config){
36675     Roo.form.TextArea.superclass.constructor.call(this, config);
36676     // these are provided exchanges for backwards compat
36677     // minHeight/maxHeight were replaced by growMin/growMax to be
36678     // compatible with TextField growing config values
36679     if(this.minHeight !== undefined){
36680         this.growMin = this.minHeight;
36681     }
36682     if(this.maxHeight !== undefined){
36683         this.growMax = this.maxHeight;
36684     }
36685 };
36686
36687 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36688     /**
36689      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36690      */
36691     growMin : 60,
36692     /**
36693      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36694      */
36695     growMax: 1000,
36696     /**
36697      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36698      * in the field (equivalent to setting overflow: hidden, defaults to false)
36699      */
36700     preventScrollbars: false,
36701     /**
36702      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36703      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36704      */
36705
36706     // private
36707     onRender : function(ct, position){
36708         if(!this.el){
36709             this.defaultAutoCreate = {
36710                 tag: "textarea",
36711                 style:"width:300px;height:60px;",
36712                 autocomplete: "off"
36713             };
36714         }
36715         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36716         if(this.grow){
36717             this.textSizeEl = Roo.DomHelper.append(document.body, {
36718                 tag: "pre", cls: "x-form-grow-sizer"
36719             });
36720             if(this.preventScrollbars){
36721                 this.el.setStyle("overflow", "hidden");
36722             }
36723             this.el.setHeight(this.growMin);
36724         }
36725     },
36726
36727     onDestroy : function(){
36728         if(this.textSizeEl){
36729             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36730         }
36731         Roo.form.TextArea.superclass.onDestroy.call(this);
36732     },
36733
36734     // private
36735     onKeyUp : function(e){
36736         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36737             this.autoSize();
36738         }
36739     },
36740
36741     /**
36742      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36743      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36744      */
36745     autoSize : function(){
36746         if(!this.grow || !this.textSizeEl){
36747             return;
36748         }
36749         var el = this.el;
36750         var v = el.dom.value;
36751         var ts = this.textSizeEl;
36752
36753         ts.innerHTML = '';
36754         ts.appendChild(document.createTextNode(v));
36755         v = ts.innerHTML;
36756
36757         Roo.fly(ts).setWidth(this.el.getWidth());
36758         if(v.length < 1){
36759             v = "&#160;&#160;";
36760         }else{
36761             if(Roo.isIE){
36762                 v = v.replace(/\n/g, '<p>&#160;</p>');
36763             }
36764             v += "&#160;\n&#160;";
36765         }
36766         ts.innerHTML = v;
36767         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36768         if(h != this.lastHeight){
36769             this.lastHeight = h;
36770             this.el.setHeight(h);
36771             this.fireEvent("autosize", this, h);
36772         }
36773     }
36774 });/*
36775  * Based on:
36776  * Ext JS Library 1.1.1
36777  * Copyright(c) 2006-2007, Ext JS, LLC.
36778  *
36779  * Originally Released Under LGPL - original licence link has changed is not relivant.
36780  *
36781  * Fork - LGPL
36782  * <script type="text/javascript">
36783  */
36784  
36785
36786 /**
36787  * @class Roo.form.NumberField
36788  * @extends Roo.form.TextField
36789  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36790  * @constructor
36791  * Creates a new NumberField
36792  * @param {Object} config Configuration options
36793  */
36794 Roo.form.NumberField = function(config){
36795     Roo.form.NumberField.superclass.constructor.call(this, config);
36796 };
36797
36798 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36799     /**
36800      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36801      */
36802     fieldClass: "x-form-field x-form-num-field",
36803     /**
36804      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36805      */
36806     allowDecimals : true,
36807     /**
36808      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36809      */
36810     decimalSeparator : ".",
36811     /**
36812      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36813      */
36814     decimalPrecision : 2,
36815     /**
36816      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36817      */
36818     allowNegative : true,
36819     /**
36820      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36821      */
36822     minValue : Number.NEGATIVE_INFINITY,
36823     /**
36824      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36825      */
36826     maxValue : Number.MAX_VALUE,
36827     /**
36828      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36829      */
36830     minText : "The minimum value for this field is {0}",
36831     /**
36832      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36833      */
36834     maxText : "The maximum value for this field is {0}",
36835     /**
36836      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36837      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36838      */
36839     nanText : "{0} is not a valid number",
36840
36841     // private
36842     initEvents : function(){
36843         Roo.form.NumberField.superclass.initEvents.call(this);
36844         var allowed = "0123456789";
36845         if(this.allowDecimals){
36846             allowed += this.decimalSeparator;
36847         }
36848         if(this.allowNegative){
36849             allowed += "-";
36850         }
36851         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36852         var keyPress = function(e){
36853             var k = e.getKey();
36854             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36855                 return;
36856             }
36857             var c = e.getCharCode();
36858             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36859                 e.stopEvent();
36860             }
36861         };
36862         this.el.on("keypress", keyPress, this);
36863     },
36864
36865     // private
36866     validateValue : function(value){
36867         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36868             return false;
36869         }
36870         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36871              return true;
36872         }
36873         var num = this.parseValue(value);
36874         if(isNaN(num)){
36875             this.markInvalid(String.format(this.nanText, value));
36876             return false;
36877         }
36878         if(num < this.minValue){
36879             this.markInvalid(String.format(this.minText, this.minValue));
36880             return false;
36881         }
36882         if(num > this.maxValue){
36883             this.markInvalid(String.format(this.maxText, this.maxValue));
36884             return false;
36885         }
36886         return true;
36887     },
36888
36889     getValue : function(){
36890         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36891     },
36892
36893     // private
36894     parseValue : function(value){
36895         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36896         return isNaN(value) ? '' : value;
36897     },
36898
36899     // private
36900     fixPrecision : function(value){
36901         var nan = isNaN(value);
36902         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36903             return nan ? '' : value;
36904         }
36905         return parseFloat(value).toFixed(this.decimalPrecision);
36906     },
36907
36908     setValue : function(v){
36909         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36910     },
36911
36912     // private
36913     decimalPrecisionFcn : function(v){
36914         return Math.floor(v);
36915     },
36916
36917     beforeBlur : function(){
36918         var v = this.parseValue(this.getRawValue());
36919         if(v){
36920             this.setValue(this.fixPrecision(v));
36921         }
36922     }
36923 });/*
36924  * Based on:
36925  * Ext JS Library 1.1.1
36926  * Copyright(c) 2006-2007, Ext JS, LLC.
36927  *
36928  * Originally Released Under LGPL - original licence link has changed is not relivant.
36929  *
36930  * Fork - LGPL
36931  * <script type="text/javascript">
36932  */
36933  
36934 /**
36935  * @class Roo.form.DateField
36936  * @extends Roo.form.TriggerField
36937  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36938 * @constructor
36939 * Create a new DateField
36940 * @param {Object} config
36941  */
36942 Roo.form.DateField = function(config){
36943     Roo.form.DateField.superclass.constructor.call(this, config);
36944     
36945       this.addEvents({
36946          
36947         /**
36948          * @event select
36949          * Fires when a date is selected
36950              * @param {Roo.form.DateField} combo This combo box
36951              * @param {Date} date The date selected
36952              */
36953         'select' : true
36954          
36955     });
36956     
36957     
36958     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36959     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36960     this.ddMatch = null;
36961     if(this.disabledDates){
36962         var dd = this.disabledDates;
36963         var re = "(?:";
36964         for(var i = 0; i < dd.length; i++){
36965             re += dd[i];
36966             if(i != dd.length-1) re += "|";
36967         }
36968         this.ddMatch = new RegExp(re + ")");
36969     }
36970 };
36971
36972 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36973     /**
36974      * @cfg {String} format
36975      * The default date format string which can be overriden for localization support.  The format must be
36976      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36977      */
36978     format : "m/d/y",
36979     /**
36980      * @cfg {String} altFormats
36981      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36982      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36983      */
36984     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36985     /**
36986      * @cfg {Array} disabledDays
36987      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36988      */
36989     disabledDays : null,
36990     /**
36991      * @cfg {String} disabledDaysText
36992      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36993      */
36994     disabledDaysText : "Disabled",
36995     /**
36996      * @cfg {Array} disabledDates
36997      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36998      * expression so they are very powerful. Some examples:
36999      * <ul>
37000      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37001      * <li>["03/08", "09/16"] would disable those days for every year</li>
37002      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37003      * <li>["03/../2006"] would disable every day in March 2006</li>
37004      * <li>["^03"] would disable every day in every March</li>
37005      * </ul>
37006      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37007      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37008      */
37009     disabledDates : null,
37010     /**
37011      * @cfg {String} disabledDatesText
37012      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37013      */
37014     disabledDatesText : "Disabled",
37015     /**
37016      * @cfg {Date/String} minValue
37017      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37018      * valid format (defaults to null).
37019      */
37020     minValue : null,
37021     /**
37022      * @cfg {Date/String} maxValue
37023      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37024      * valid format (defaults to null).
37025      */
37026     maxValue : null,
37027     /**
37028      * @cfg {String} minText
37029      * The error text to display when the date in the cell is before minValue (defaults to
37030      * 'The date in this field must be after {minValue}').
37031      */
37032     minText : "The date in this field must be equal to or after {0}",
37033     /**
37034      * @cfg {String} maxText
37035      * The error text to display when the date in the cell is after maxValue (defaults to
37036      * 'The date in this field must be before {maxValue}').
37037      */
37038     maxText : "The date in this field must be equal to or before {0}",
37039     /**
37040      * @cfg {String} invalidText
37041      * The error text to display when the date in the field is invalid (defaults to
37042      * '{value} is not a valid date - it must be in the format {format}').
37043      */
37044     invalidText : "{0} is not a valid date - it must be in the format {1}",
37045     /**
37046      * @cfg {String} triggerClass
37047      * An additional CSS class used to style the trigger button.  The trigger will always get the
37048      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37049      * which displays a calendar icon).
37050      */
37051     triggerClass : 'x-form-date-trigger',
37052     
37053
37054     /**
37055      * @cfg {bool} useIso
37056      * if enabled, then the date field will use a hidden field to store the 
37057      * real value as iso formated date. default (false)
37058      */ 
37059     useIso : false,
37060     /**
37061      * @cfg {String/Object} autoCreate
37062      * A DomHelper element spec, or true for a default element spec (defaults to
37063      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37064      */ 
37065     // private
37066     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37067     
37068     // private
37069     hiddenField: false,
37070     
37071     onRender : function(ct, position)
37072     {
37073         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37074         if (this.useIso) {
37075             this.el.dom.removeAttribute('name'); 
37076             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37077                     'before', true);
37078             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37079             // prevent input submission
37080             this.hiddenName = this.name;
37081         }
37082             
37083             
37084     },
37085     
37086     // private
37087     validateValue : function(value)
37088     {
37089         value = this.formatDate(value);
37090         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37091             return false;
37092         }
37093         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37094              return true;
37095         }
37096         var svalue = value;
37097         value = this.parseDate(value);
37098         if(!value){
37099             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37100             return false;
37101         }
37102         var time = value.getTime();
37103         if(this.minValue && time < this.minValue.getTime()){
37104             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37105             return false;
37106         }
37107         if(this.maxValue && time > this.maxValue.getTime()){
37108             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37109             return false;
37110         }
37111         if(this.disabledDays){
37112             var day = value.getDay();
37113             for(var i = 0; i < this.disabledDays.length; i++) {
37114                 if(day === this.disabledDays[i]){
37115                     this.markInvalid(this.disabledDaysText);
37116                     return false;
37117                 }
37118             }
37119         }
37120         var fvalue = this.formatDate(value);
37121         if(this.ddMatch && this.ddMatch.test(fvalue)){
37122             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37123             return false;
37124         }
37125         return true;
37126     },
37127
37128     // private
37129     // Provides logic to override the default TriggerField.validateBlur which just returns true
37130     validateBlur : function(){
37131         return !this.menu || !this.menu.isVisible();
37132     },
37133
37134     /**
37135      * Returns the current date value of the date field.
37136      * @return {Date} The date value
37137      */
37138     getValue : function(){
37139         
37140         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37141     },
37142
37143     /**
37144      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37145      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37146      * (the default format used is "m/d/y").
37147      * <br />Usage:
37148      * <pre><code>
37149 //All of these calls set the same date value (May 4, 2006)
37150
37151 //Pass a date object:
37152 var dt = new Date('5/4/06');
37153 dateField.setValue(dt);
37154
37155 //Pass a date string (default format):
37156 dateField.setValue('5/4/06');
37157
37158 //Pass a date string (custom format):
37159 dateField.format = 'Y-m-d';
37160 dateField.setValue('2006-5-4');
37161 </code></pre>
37162      * @param {String/Date} date The date or valid date string
37163      */
37164     setValue : function(date){
37165         if (this.hiddenField) {
37166             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37167         }
37168         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37169     },
37170
37171     // private
37172     parseDate : function(value){
37173         if(!value || value instanceof Date){
37174             return value;
37175         }
37176         var v = Date.parseDate(value, this.format);
37177         if(!v && this.altFormats){
37178             if(!this.altFormatsArray){
37179                 this.altFormatsArray = this.altFormats.split("|");
37180             }
37181             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37182                 v = Date.parseDate(value, this.altFormatsArray[i]);
37183             }
37184         }
37185         return v;
37186     },
37187
37188     // private
37189     formatDate : function(date, fmt){
37190         return (!date || !(date instanceof Date)) ?
37191                date : date.dateFormat(fmt || this.format);
37192     },
37193
37194     // private
37195     menuListeners : {
37196         select: function(m, d){
37197             this.setValue(d);
37198             this.fireEvent('select', this, d);
37199         },
37200         show : function(){ // retain focus styling
37201             this.onFocus();
37202         },
37203         hide : function(){
37204             this.focus.defer(10, this);
37205             var ml = this.menuListeners;
37206             this.menu.un("select", ml.select,  this);
37207             this.menu.un("show", ml.show,  this);
37208             this.menu.un("hide", ml.hide,  this);
37209         }
37210     },
37211
37212     // private
37213     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37214     onTriggerClick : function(){
37215         if(this.disabled){
37216             return;
37217         }
37218         if(this.menu == null){
37219             this.menu = new Roo.menu.DateMenu();
37220         }
37221         Roo.apply(this.menu.picker,  {
37222             showClear: this.allowBlank,
37223             minDate : this.minValue,
37224             maxDate : this.maxValue,
37225             disabledDatesRE : this.ddMatch,
37226             disabledDatesText : this.disabledDatesText,
37227             disabledDays : this.disabledDays,
37228             disabledDaysText : this.disabledDaysText,
37229             format : this.format,
37230             minText : String.format(this.minText, this.formatDate(this.minValue)),
37231             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37232         });
37233         this.menu.on(Roo.apply({}, this.menuListeners, {
37234             scope:this
37235         }));
37236         this.menu.picker.setValue(this.getValue() || new Date());
37237         this.menu.show(this.el, "tl-bl?");
37238     },
37239
37240     beforeBlur : function(){
37241         var v = this.parseDate(this.getRawValue());
37242         if(v){
37243             this.setValue(v);
37244         }
37245     }
37246
37247     /** @cfg {Boolean} grow @hide */
37248     /** @cfg {Number} growMin @hide */
37249     /** @cfg {Number} growMax @hide */
37250     /**
37251      * @hide
37252      * @method autoSize
37253      */
37254 });/*
37255  * Based on:
37256  * Ext JS Library 1.1.1
37257  * Copyright(c) 2006-2007, Ext JS, LLC.
37258  *
37259  * Originally Released Under LGPL - original licence link has changed is not relivant.
37260  *
37261  * Fork - LGPL
37262  * <script type="text/javascript">
37263  */
37264  
37265
37266 /**
37267  * @class Roo.form.ComboBox
37268  * @extends Roo.form.TriggerField
37269  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37270  * @constructor
37271  * Create a new ComboBox.
37272  * @param {Object} config Configuration options
37273  */
37274 Roo.form.ComboBox = function(config){
37275     Roo.form.ComboBox.superclass.constructor.call(this, config);
37276     this.addEvents({
37277         /**
37278          * @event expand
37279          * Fires when the dropdown list is expanded
37280              * @param {Roo.form.ComboBox} combo This combo box
37281              */
37282         'expand' : true,
37283         /**
37284          * @event collapse
37285          * Fires when the dropdown list is collapsed
37286              * @param {Roo.form.ComboBox} combo This combo box
37287              */
37288         'collapse' : true,
37289         /**
37290          * @event beforeselect
37291          * Fires before a list item is selected. Return false to cancel the selection.
37292              * @param {Roo.form.ComboBox} combo This combo box
37293              * @param {Roo.data.Record} record The data record returned from the underlying store
37294              * @param {Number} index The index of the selected item in the dropdown list
37295              */
37296         'beforeselect' : true,
37297         /**
37298          * @event select
37299          * Fires when a list item is selected
37300              * @param {Roo.form.ComboBox} combo This combo box
37301              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37302              * @param {Number} index The index of the selected item in the dropdown list
37303              */
37304         'select' : true,
37305         /**
37306          * @event beforequery
37307          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37308          * The event object passed has these properties:
37309              * @param {Roo.form.ComboBox} combo This combo box
37310              * @param {String} query The query
37311              * @param {Boolean} forceAll true to force "all" query
37312              * @param {Boolean} cancel true to cancel the query
37313              * @param {Object} e The query event object
37314              */
37315         'beforequery': true,
37316          /**
37317          * @event add
37318          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37319              * @param {Roo.form.ComboBox} combo This combo box
37320              */
37321         'add' : true,
37322         /**
37323          * @event edit
37324          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37325              * @param {Roo.form.ComboBox} combo This combo box
37326              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37327              */
37328         'edit' : true
37329         
37330         
37331     });
37332     if(this.transform){
37333         this.allowDomMove = false;
37334         var s = Roo.getDom(this.transform);
37335         if(!this.hiddenName){
37336             this.hiddenName = s.name;
37337         }
37338         if(!this.store){
37339             this.mode = 'local';
37340             var d = [], opts = s.options;
37341             for(var i = 0, len = opts.length;i < len; i++){
37342                 var o = opts[i];
37343                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37344                 if(o.selected) {
37345                     this.value = value;
37346                 }
37347                 d.push([value, o.text]);
37348             }
37349             this.store = new Roo.data.SimpleStore({
37350                 'id': 0,
37351                 fields: ['value', 'text'],
37352                 data : d
37353             });
37354             this.valueField = 'value';
37355             this.displayField = 'text';
37356         }
37357         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37358         if(!this.lazyRender){
37359             this.target = true;
37360             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37361             s.parentNode.removeChild(s); // remove it
37362             this.render(this.el.parentNode);
37363         }else{
37364             s.parentNode.removeChild(s); // remove it
37365         }
37366
37367     }
37368     if (this.store) {
37369         this.store = Roo.factory(this.store, Roo.data);
37370     }
37371     
37372     this.selectedIndex = -1;
37373     if(this.mode == 'local'){
37374         if(config.queryDelay === undefined){
37375             this.queryDelay = 10;
37376         }
37377         if(config.minChars === undefined){
37378             this.minChars = 0;
37379         }
37380     }
37381 };
37382
37383 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37384     /**
37385      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37386      */
37387     /**
37388      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37389      * rendering into an Roo.Editor, defaults to false)
37390      */
37391     /**
37392      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37393      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37394      */
37395     /**
37396      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37397      */
37398     /**
37399      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37400      * the dropdown list (defaults to undefined, with no header element)
37401      */
37402
37403      /**
37404      * @cfg {String/Roo.Template} tpl The template to use to render the output
37405      */
37406      
37407     // private
37408     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37409     /**
37410      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37411      */
37412     listWidth: undefined,
37413     /**
37414      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37415      * mode = 'remote' or 'text' if mode = 'local')
37416      */
37417     displayField: undefined,
37418     /**
37419      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37420      * mode = 'remote' or 'value' if mode = 'local'). 
37421      * Note: use of a valueField requires the user make a selection
37422      * in order for a value to be mapped.
37423      */
37424     valueField: undefined,
37425     
37426     
37427     /**
37428      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37429      * field's data value (defaults to the underlying DOM element's name)
37430      */
37431     hiddenName: undefined,
37432     /**
37433      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37434      */
37435     listClass: '',
37436     /**
37437      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37438      */
37439     selectedClass: 'x-combo-selected',
37440     /**
37441      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37442      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37443      * which displays a downward arrow icon).
37444      */
37445     triggerClass : 'x-form-arrow-trigger',
37446     /**
37447      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37448      */
37449     shadow:'sides',
37450     /**
37451      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37452      * anchor positions (defaults to 'tl-bl')
37453      */
37454     listAlign: 'tl-bl?',
37455     /**
37456      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37457      */
37458     maxHeight: 300,
37459     /**
37460      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37461      * query specified by the allQuery config option (defaults to 'query')
37462      */
37463     triggerAction: 'query',
37464     /**
37465      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37466      * (defaults to 4, does not apply if editable = false)
37467      */
37468     minChars : 4,
37469     /**
37470      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37471      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37472      */
37473     typeAhead: false,
37474     /**
37475      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37476      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37477      */
37478     queryDelay: 500,
37479     /**
37480      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37481      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37482      */
37483     pageSize: 0,
37484     /**
37485      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37486      * when editable = true (defaults to false)
37487      */
37488     selectOnFocus:false,
37489     /**
37490      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37491      */
37492     queryParam: 'query',
37493     /**
37494      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37495      * when mode = 'remote' (defaults to 'Loading...')
37496      */
37497     loadingText: 'Loading...',
37498     /**
37499      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37500      */
37501     resizable: false,
37502     /**
37503      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37504      */
37505     handleHeight : 8,
37506     /**
37507      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37508      * traditional select (defaults to true)
37509      */
37510     editable: true,
37511     /**
37512      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37513      */
37514     allQuery: '',
37515     /**
37516      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37517      */
37518     mode: 'remote',
37519     /**
37520      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37521      * listWidth has a higher value)
37522      */
37523     minListWidth : 70,
37524     /**
37525      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37526      * allow the user to set arbitrary text into the field (defaults to false)
37527      */
37528     forceSelection:false,
37529     /**
37530      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37531      * if typeAhead = true (defaults to 250)
37532      */
37533     typeAheadDelay : 250,
37534     /**
37535      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37536      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37537      */
37538     valueNotFoundText : undefined,
37539     /**
37540      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37541      */
37542     blockFocus : false,
37543     
37544     /**
37545      * @cfg {Boolean} disableClear Disable showing of clear button.
37546      */
37547     disableClear : false,
37548     /**
37549      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37550      */
37551     alwaysQuery : false,
37552     
37553     //private
37554     addicon : false,
37555     editicon: false,
37556     
37557     // element that contains real text value.. (when hidden is used..)
37558      
37559     // private
37560     onRender : function(ct, position){
37561         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37562         if(this.hiddenName){
37563             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37564                     'before', true);
37565             this.hiddenField.value =
37566                 this.hiddenValue !== undefined ? this.hiddenValue :
37567                 this.value !== undefined ? this.value : '';
37568
37569             // prevent input submission
37570             this.el.dom.removeAttribute('name');
37571              
37572              
37573         }
37574         if(Roo.isGecko){
37575             this.el.dom.setAttribute('autocomplete', 'off');
37576         }
37577
37578         var cls = 'x-combo-list';
37579
37580         this.list = new Roo.Layer({
37581             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37582         });
37583
37584         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37585         this.list.setWidth(lw);
37586         this.list.swallowEvent('mousewheel');
37587         this.assetHeight = 0;
37588
37589         if(this.title){
37590             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37591             this.assetHeight += this.header.getHeight();
37592         }
37593
37594         this.innerList = this.list.createChild({cls:cls+'-inner'});
37595         this.innerList.on('mouseover', this.onViewOver, this);
37596         this.innerList.on('mousemove', this.onViewMove, this);
37597         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37598         
37599         if(this.allowBlank && !this.pageSize && !this.disableClear){
37600             this.footer = this.list.createChild({cls:cls+'-ft'});
37601             this.pageTb = new Roo.Toolbar(this.footer);
37602            
37603         }
37604         if(this.pageSize){
37605             this.footer = this.list.createChild({cls:cls+'-ft'});
37606             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37607                     {pageSize: this.pageSize});
37608             
37609         }
37610         
37611         if (this.pageTb && this.allowBlank && !this.disableClear) {
37612             var _this = this;
37613             this.pageTb.add(new Roo.Toolbar.Fill(), {
37614                 cls: 'x-btn-icon x-btn-clear',
37615                 text: '&#160;',
37616                 handler: function()
37617                 {
37618                     _this.collapse();
37619                     _this.clearValue();
37620                     _this.onSelect(false, -1);
37621                 }
37622             });
37623         }
37624         if (this.footer) {
37625             this.assetHeight += this.footer.getHeight();
37626         }
37627         
37628
37629         if(!this.tpl){
37630             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37631         }
37632
37633         this.view = new Roo.View(this.innerList, this.tpl, {
37634             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37635         });
37636
37637         this.view.on('click', this.onViewClick, this);
37638
37639         this.store.on('beforeload', this.onBeforeLoad, this);
37640         this.store.on('load', this.onLoad, this);
37641         this.store.on('loadexception', this.collapse, this);
37642
37643         if(this.resizable){
37644             this.resizer = new Roo.Resizable(this.list,  {
37645                pinned:true, handles:'se'
37646             });
37647             this.resizer.on('resize', function(r, w, h){
37648                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37649                 this.listWidth = w;
37650                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37651                 this.restrictHeight();
37652             }, this);
37653             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37654         }
37655         if(!this.editable){
37656             this.editable = true;
37657             this.setEditable(false);
37658         }  
37659         
37660         
37661         if (typeof(this.events.add.listeners) != 'undefined') {
37662             
37663             this.addicon = this.wrap.createChild(
37664                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37665        
37666             this.addicon.on('click', function(e) {
37667                 this.fireEvent('add', this);
37668             }, this);
37669         }
37670         if (typeof(this.events.edit.listeners) != 'undefined') {
37671             
37672             this.editicon = this.wrap.createChild(
37673                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37674             if (this.addicon) {
37675                 this.editicon.setStyle('margin-left', '40px');
37676             }
37677             this.editicon.on('click', function(e) {
37678                 
37679                 // we fire even  if inothing is selected..
37680                 this.fireEvent('edit', this, this.lastData );
37681                 
37682             }, this);
37683         }
37684         
37685         
37686         
37687     },
37688
37689     // private
37690     initEvents : function(){
37691         Roo.form.ComboBox.superclass.initEvents.call(this);
37692
37693         this.keyNav = new Roo.KeyNav(this.el, {
37694             "up" : function(e){
37695                 this.inKeyMode = true;
37696                 this.selectPrev();
37697             },
37698
37699             "down" : function(e){
37700                 if(!this.isExpanded()){
37701                     this.onTriggerClick();
37702                 }else{
37703                     this.inKeyMode = true;
37704                     this.selectNext();
37705                 }
37706             },
37707
37708             "enter" : function(e){
37709                 this.onViewClick();
37710                 //return true;
37711             },
37712
37713             "esc" : function(e){
37714                 this.collapse();
37715             },
37716
37717             "tab" : function(e){
37718                 this.onViewClick(false);
37719                 this.fireEvent("specialkey", this, e);
37720                 return true;
37721             },
37722
37723             scope : this,
37724
37725             doRelay : function(foo, bar, hname){
37726                 if(hname == 'down' || this.scope.isExpanded()){
37727                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37728                 }
37729                 return true;
37730             },
37731
37732             forceKeyDown: true
37733         });
37734         this.queryDelay = Math.max(this.queryDelay || 10,
37735                 this.mode == 'local' ? 10 : 250);
37736         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37737         if(this.typeAhead){
37738             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37739         }
37740         if(this.editable !== false){
37741             this.el.on("keyup", this.onKeyUp, this);
37742         }
37743         if(this.forceSelection){
37744             this.on('blur', this.doForce, this);
37745         }
37746     },
37747
37748     onDestroy : function(){
37749         if(this.view){
37750             this.view.setStore(null);
37751             this.view.el.removeAllListeners();
37752             this.view.el.remove();
37753             this.view.purgeListeners();
37754         }
37755         if(this.list){
37756             this.list.destroy();
37757         }
37758         if(this.store){
37759             this.store.un('beforeload', this.onBeforeLoad, this);
37760             this.store.un('load', this.onLoad, this);
37761             this.store.un('loadexception', this.collapse, this);
37762         }
37763         Roo.form.ComboBox.superclass.onDestroy.call(this);
37764     },
37765
37766     // private
37767     fireKey : function(e){
37768         if(e.isNavKeyPress() && !this.list.isVisible()){
37769             this.fireEvent("specialkey", this, e);
37770         }
37771     },
37772
37773     // private
37774     onResize: function(w, h){
37775         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37776         
37777         if(typeof w != 'number'){
37778             // we do not handle it!?!?
37779             return;
37780         }
37781         var tw = this.trigger.getWidth();
37782         tw += this.addicon ? this.addicon.getWidth() : 0;
37783         tw += this.editicon ? this.editicon.getWidth() : 0;
37784         var x = w - tw;
37785         this.el.setWidth( this.adjustWidth('input', x));
37786             
37787         this.trigger.setStyle('left', x+'px');
37788         
37789         if(this.list && this.listWidth === undefined){
37790             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37791             this.list.setWidth(lw);
37792             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37793         }
37794         
37795     
37796         
37797     },
37798
37799     /**
37800      * Allow or prevent the user from directly editing the field text.  If false is passed,
37801      * the user will only be able to select from the items defined in the dropdown list.  This method
37802      * is the runtime equivalent of setting the 'editable' config option at config time.
37803      * @param {Boolean} value True to allow the user to directly edit the field text
37804      */
37805     setEditable : function(value){
37806         if(value == this.editable){
37807             return;
37808         }
37809         this.editable = value;
37810         if(!value){
37811             this.el.dom.setAttribute('readOnly', true);
37812             this.el.on('mousedown', this.onTriggerClick,  this);
37813             this.el.addClass('x-combo-noedit');
37814         }else{
37815             this.el.dom.setAttribute('readOnly', false);
37816             this.el.un('mousedown', this.onTriggerClick,  this);
37817             this.el.removeClass('x-combo-noedit');
37818         }
37819     },
37820
37821     // private
37822     onBeforeLoad : function(){
37823         if(!this.hasFocus){
37824             return;
37825         }
37826         this.innerList.update(this.loadingText ?
37827                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37828         this.restrictHeight();
37829         this.selectedIndex = -1;
37830     },
37831
37832     // private
37833     onLoad : function(){
37834         if(!this.hasFocus){
37835             return;
37836         }
37837         if(this.store.getCount() > 0){
37838             this.expand();
37839             this.restrictHeight();
37840             if(this.lastQuery == this.allQuery){
37841                 if(this.editable){
37842                     this.el.dom.select();
37843                 }
37844                 if(!this.selectByValue(this.value, true)){
37845                     this.select(0, true);
37846                 }
37847             }else{
37848                 this.selectNext();
37849                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37850                     this.taTask.delay(this.typeAheadDelay);
37851                 }
37852             }
37853         }else{
37854             this.onEmptyResults();
37855         }
37856         //this.el.focus();
37857     },
37858
37859     // private
37860     onTypeAhead : function(){
37861         if(this.store.getCount() > 0){
37862             var r = this.store.getAt(0);
37863             var newValue = r.data[this.displayField];
37864             var len = newValue.length;
37865             var selStart = this.getRawValue().length;
37866             if(selStart != len){
37867                 this.setRawValue(newValue);
37868                 this.selectText(selStart, newValue.length);
37869             }
37870         }
37871     },
37872
37873     // private
37874     onSelect : function(record, index){
37875         if(this.fireEvent('beforeselect', this, record, index) !== false){
37876             this.setFromData(index > -1 ? record.data : false);
37877             this.collapse();
37878             this.fireEvent('select', this, record, index);
37879         }
37880     },
37881
37882     /**
37883      * Returns the currently selected field value or empty string if no value is set.
37884      * @return {String} value The selected value
37885      */
37886     getValue : function(){
37887         if(this.valueField){
37888             return typeof this.value != 'undefined' ? this.value : '';
37889         }else{
37890             return Roo.form.ComboBox.superclass.getValue.call(this);
37891         }
37892     },
37893
37894     /**
37895      * Clears any text/value currently set in the field
37896      */
37897     clearValue : function(){
37898         if(this.hiddenField){
37899             this.hiddenField.value = '';
37900         }
37901         this.value = '';
37902         this.setRawValue('');
37903         this.lastSelectionText = '';
37904         this.applyEmptyText();
37905     },
37906
37907     /**
37908      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37909      * will be displayed in the field.  If the value does not match the data value of an existing item,
37910      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37911      * Otherwise the field will be blank (although the value will still be set).
37912      * @param {String} value The value to match
37913      */
37914     setValue : function(v){
37915         var text = v;
37916         if(this.valueField){
37917             var r = this.findRecord(this.valueField, v);
37918             if(r){
37919                 text = r.data[this.displayField];
37920             }else if(this.valueNotFoundText !== undefined){
37921                 text = this.valueNotFoundText;
37922             }
37923         }
37924         this.lastSelectionText = text;
37925         if(this.hiddenField){
37926             this.hiddenField.value = v;
37927         }
37928         Roo.form.ComboBox.superclass.setValue.call(this, text);
37929         this.value = v;
37930     },
37931     /**
37932      * @property {Object} the last set data for the element
37933      */
37934     
37935     lastData : false,
37936     /**
37937      * Sets the value of the field based on a object which is related to the record format for the store.
37938      * @param {Object} value the value to set as. or false on reset?
37939      */
37940     setFromData : function(o){
37941         var dv = ''; // display value
37942         var vv = ''; // value value..
37943         this.lastData = o;
37944         if (this.displayField) {
37945             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37946         } else {
37947             // this is an error condition!!!
37948             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37949         }
37950         
37951         if(this.valueField){
37952             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37953         }
37954         if(this.hiddenField){
37955             this.hiddenField.value = vv;
37956             
37957             this.lastSelectionText = dv;
37958             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37959             this.value = vv;
37960             return;
37961         }
37962         // no hidden field.. - we store the value in 'value', but still display
37963         // display field!!!!
37964         this.lastSelectionText = dv;
37965         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37966         this.value = vv;
37967         
37968         
37969     },
37970     // private
37971     reset : function(){
37972         // overridden so that last data is reset..
37973         this.setValue(this.originalValue);
37974         this.clearInvalid();
37975         this.lastData = false;
37976     },
37977     // private
37978     findRecord : function(prop, value){
37979         var record;
37980         if(this.store.getCount() > 0){
37981             this.store.each(function(r){
37982                 if(r.data[prop] == value){
37983                     record = r;
37984                     return false;
37985                 }
37986                 return true;
37987             });
37988         }
37989         return record;
37990     },
37991     
37992     getName: function()
37993     {
37994         // returns hidden if it's set..
37995         if (!this.rendered) {return ''};
37996         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37997         
37998     },
37999     // private
38000     onViewMove : function(e, t){
38001         this.inKeyMode = false;
38002     },
38003
38004     // private
38005     onViewOver : function(e, t){
38006         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38007             return;
38008         }
38009         var item = this.view.findItemFromChild(t);
38010         if(item){
38011             var index = this.view.indexOf(item);
38012             this.select(index, false);
38013         }
38014     },
38015
38016     // private
38017     onViewClick : function(doFocus)
38018     {
38019         var index = this.view.getSelectedIndexes()[0];
38020         var r = this.store.getAt(index);
38021         if(r){
38022             this.onSelect(r, index);
38023         }
38024         if(doFocus !== false && !this.blockFocus){
38025             this.el.focus();
38026         }
38027     },
38028
38029     // private
38030     restrictHeight : function(){
38031         this.innerList.dom.style.height = '';
38032         var inner = this.innerList.dom;
38033         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38034         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38035         this.list.beginUpdate();
38036         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38037         this.list.alignTo(this.el, this.listAlign);
38038         this.list.endUpdate();
38039     },
38040
38041     // private
38042     onEmptyResults : function(){
38043         this.collapse();
38044     },
38045
38046     /**
38047      * Returns true if the dropdown list is expanded, else false.
38048      */
38049     isExpanded : function(){
38050         return this.list.isVisible();
38051     },
38052
38053     /**
38054      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38055      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38056      * @param {String} value The data value of the item to select
38057      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38058      * selected item if it is not currently in view (defaults to true)
38059      * @return {Boolean} True if the value matched an item in the list, else false
38060      */
38061     selectByValue : function(v, scrollIntoView){
38062         if(v !== undefined && v !== null){
38063             var r = this.findRecord(this.valueField || this.displayField, v);
38064             if(r){
38065                 this.select(this.store.indexOf(r), scrollIntoView);
38066                 return true;
38067             }
38068         }
38069         return false;
38070     },
38071
38072     /**
38073      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38074      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38075      * @param {Number} index The zero-based index of the list item to select
38076      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38077      * selected item if it is not currently in view (defaults to true)
38078      */
38079     select : function(index, scrollIntoView){
38080         this.selectedIndex = index;
38081         this.view.select(index);
38082         if(scrollIntoView !== false){
38083             var el = this.view.getNode(index);
38084             if(el){
38085                 this.innerList.scrollChildIntoView(el, false);
38086             }
38087         }
38088     },
38089
38090     // private
38091     selectNext : function(){
38092         var ct = this.store.getCount();
38093         if(ct > 0){
38094             if(this.selectedIndex == -1){
38095                 this.select(0);
38096             }else if(this.selectedIndex < ct-1){
38097                 this.select(this.selectedIndex+1);
38098             }
38099         }
38100     },
38101
38102     // private
38103     selectPrev : function(){
38104         var ct = this.store.getCount();
38105         if(ct > 0){
38106             if(this.selectedIndex == -1){
38107                 this.select(0);
38108             }else if(this.selectedIndex != 0){
38109                 this.select(this.selectedIndex-1);
38110             }
38111         }
38112     },
38113
38114     // private
38115     onKeyUp : function(e){
38116         if(this.editable !== false && !e.isSpecialKey()){
38117             this.lastKey = e.getKey();
38118             this.dqTask.delay(this.queryDelay);
38119         }
38120     },
38121
38122     // private
38123     validateBlur : function(){
38124         return !this.list || !this.list.isVisible();   
38125     },
38126
38127     // private
38128     initQuery : function(){
38129         this.doQuery(this.getRawValue());
38130     },
38131
38132     // private
38133     doForce : function(){
38134         if(this.el.dom.value.length > 0){
38135             this.el.dom.value =
38136                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38137             this.applyEmptyText();
38138         }
38139     },
38140
38141     /**
38142      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38143      * query allowing the query action to be canceled if needed.
38144      * @param {String} query The SQL query to execute
38145      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38146      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38147      * saved in the current store (defaults to false)
38148      */
38149     doQuery : function(q, forceAll){
38150         if(q === undefined || q === null){
38151             q = '';
38152         }
38153         var qe = {
38154             query: q,
38155             forceAll: forceAll,
38156             combo: this,
38157             cancel:false
38158         };
38159         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38160             return false;
38161         }
38162         q = qe.query;
38163         forceAll = qe.forceAll;
38164         if(forceAll === true || (q.length >= this.minChars)){
38165             if(this.lastQuery != q || this.alwaysQuery){
38166                 this.lastQuery = q;
38167                 if(this.mode == 'local'){
38168                     this.selectedIndex = -1;
38169                     if(forceAll){
38170                         this.store.clearFilter();
38171                     }else{
38172                         this.store.filter(this.displayField, q);
38173                     }
38174                     this.onLoad();
38175                 }else{
38176                     this.store.baseParams[this.queryParam] = q;
38177                     this.store.load({
38178                         params: this.getParams(q)
38179                     });
38180                     this.expand();
38181                 }
38182             }else{
38183                 this.selectedIndex = -1;
38184                 this.onLoad();   
38185             }
38186         }
38187     },
38188
38189     // private
38190     getParams : function(q){
38191         var p = {};
38192         //p[this.queryParam] = q;
38193         if(this.pageSize){
38194             p.start = 0;
38195             p.limit = this.pageSize;
38196         }
38197         return p;
38198     },
38199
38200     /**
38201      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38202      */
38203     collapse : function(){
38204         if(!this.isExpanded()){
38205             return;
38206         }
38207         this.list.hide();
38208         Roo.get(document).un('mousedown', this.collapseIf, this);
38209         Roo.get(document).un('mousewheel', this.collapseIf, this);
38210         if (!this.editable) {
38211             Roo.get(document).un('keydown', this.listKeyPress, this);
38212         }
38213         this.fireEvent('collapse', this);
38214     },
38215
38216     // private
38217     collapseIf : function(e){
38218         if(!e.within(this.wrap) && !e.within(this.list)){
38219             this.collapse();
38220         }
38221     },
38222
38223     /**
38224      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38225      */
38226     expand : function(){
38227         if(this.isExpanded() || !this.hasFocus){
38228             return;
38229         }
38230         this.list.alignTo(this.el, this.listAlign);
38231         this.list.show();
38232         Roo.get(document).on('mousedown', this.collapseIf, this);
38233         Roo.get(document).on('mousewheel', this.collapseIf, this);
38234         if (!this.editable) {
38235             Roo.get(document).on('keydown', this.listKeyPress, this);
38236         }
38237         
38238         this.fireEvent('expand', this);
38239     },
38240
38241     // private
38242     // Implements the default empty TriggerField.onTriggerClick function
38243     onTriggerClick : function(){
38244         if(this.disabled){
38245             return;
38246         }
38247         if(this.isExpanded()){
38248             this.collapse();
38249             if (!this.blockFocus) {
38250                 this.el.focus();
38251             }
38252             
38253         }else {
38254             this.hasFocus = true;
38255             if(this.triggerAction == 'all') {
38256                 this.doQuery(this.allQuery, true);
38257             } else {
38258                 this.doQuery(this.getRawValue());
38259             }
38260             if (!this.blockFocus) {
38261                 this.el.focus();
38262             }
38263         }
38264     },
38265     listKeyPress : function(e)
38266     {
38267         //Roo.log('listkeypress');
38268         // scroll to first matching element based on key pres..
38269         if (e.isSpecialKey()) {
38270             return false;
38271         }
38272         var k = String.fromCharCode(e.getKey()).toUpperCase();
38273         //Roo.log(k);
38274         var match  = false;
38275         var csel = this.view.getSelectedNodes();
38276         var cselitem = false;
38277         if (csel.length) {
38278             var ix = this.view.indexOf(csel[0]);
38279             cselitem  = this.store.getAt(ix);
38280             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38281                 cselitem = false;
38282             }
38283             
38284         }
38285         
38286         this.store.each(function(v) { 
38287             if (cselitem) {
38288                 // start at existing selection.
38289                 if (cselitem.id == v.id) {
38290                     cselitem = false;
38291                 }
38292                 return;
38293             }
38294                 
38295             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38296                 match = this.store.indexOf(v);
38297                 return false;
38298             }
38299         }, this);
38300         
38301         if (match === false) {
38302             return true; // no more action?
38303         }
38304         // scroll to?
38305         this.view.select(match);
38306         var sn = Roo.get(this.view.getSelectedNodes()[0])
38307         sn.scrollIntoView(sn.dom.parentNode, false);
38308     }
38309
38310     /** 
38311     * @cfg {Boolean} grow 
38312     * @hide 
38313     */
38314     /** 
38315     * @cfg {Number} growMin 
38316     * @hide 
38317     */
38318     /** 
38319     * @cfg {Number} growMax 
38320     * @hide 
38321     */
38322     /**
38323      * @hide
38324      * @method autoSize
38325      */
38326 });/*
38327  * Based on:
38328  * Ext JS Library 1.1.1
38329  * Copyright(c) 2006-2007, Ext JS, LLC.
38330  *
38331  * Originally Released Under LGPL - original licence link has changed is not relivant.
38332  *
38333  * Fork - LGPL
38334  * <script type="text/javascript">
38335  */
38336 /**
38337  * @class Roo.form.Checkbox
38338  * @extends Roo.form.Field
38339  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38340  * @constructor
38341  * Creates a new Checkbox
38342  * @param {Object} config Configuration options
38343  */
38344 Roo.form.Checkbox = function(config){
38345     Roo.form.Checkbox.superclass.constructor.call(this, config);
38346     this.addEvents({
38347         /**
38348          * @event check
38349          * Fires when the checkbox is checked or unchecked.
38350              * @param {Roo.form.Checkbox} this This checkbox
38351              * @param {Boolean} checked The new checked value
38352              */
38353         check : true
38354     });
38355 };
38356
38357 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38358     /**
38359      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38360      */
38361     focusClass : undefined,
38362     /**
38363      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38364      */
38365     fieldClass: "x-form-field",
38366     /**
38367      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38368      */
38369     checked: false,
38370     /**
38371      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38372      * {tag: "input", type: "checkbox", autocomplete: "off"})
38373      */
38374     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38375     /**
38376      * @cfg {String} boxLabel The text that appears beside the checkbox
38377      */
38378     boxLabel : "",
38379     /**
38380      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38381      */  
38382     inputValue : '1',
38383     /**
38384      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38385      */
38386      valueOff: '0', // value when not checked..
38387
38388     actionMode : 'viewEl', 
38389     //
38390     // private
38391     itemCls : 'x-menu-check-item x-form-item',
38392     groupClass : 'x-menu-group-item',
38393     inputType : 'hidden',
38394     
38395     
38396     inSetChecked: false, // check that we are not calling self...
38397     
38398     inputElement: false, // real input element?
38399     basedOn: false, // ????
38400     
38401     isFormField: true, // not sure where this is needed!!!!
38402
38403     onResize : function(){
38404         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38405         if(!this.boxLabel){
38406             this.el.alignTo(this.wrap, 'c-c');
38407         }
38408     },
38409
38410     initEvents : function(){
38411         Roo.form.Checkbox.superclass.initEvents.call(this);
38412         this.el.on("click", this.onClick,  this);
38413         this.el.on("change", this.onClick,  this);
38414     },
38415
38416
38417     getResizeEl : function(){
38418         return this.wrap;
38419     },
38420
38421     getPositionEl : function(){
38422         return this.wrap;
38423     },
38424
38425     // private
38426     onRender : function(ct, position){
38427         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38428         /*
38429         if(this.inputValue !== undefined){
38430             this.el.dom.value = this.inputValue;
38431         }
38432         */
38433         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38434         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38435         var viewEl = this.wrap.createChild({ 
38436             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38437         this.viewEl = viewEl;   
38438         this.wrap.on('click', this.onClick,  this); 
38439         
38440         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38441         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38442         
38443         
38444         
38445         if(this.boxLabel){
38446             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38447         //    viewEl.on('click', this.onClick,  this); 
38448         }
38449         //if(this.checked){
38450             this.setChecked(this.checked);
38451         //}else{
38452             //this.checked = this.el.dom;
38453         //}
38454
38455     },
38456
38457     // private
38458     initValue : Roo.emptyFn,
38459
38460     /**
38461      * Returns the checked state of the checkbox.
38462      * @return {Boolean} True if checked, else false
38463      */
38464     getValue : function(){
38465         if(this.el){
38466             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38467         }
38468         return this.valueOff;
38469         
38470     },
38471
38472         // private
38473     onClick : function(){ 
38474         this.setChecked(!this.checked);
38475
38476         //if(this.el.dom.checked != this.checked){
38477         //    this.setValue(this.el.dom.checked);
38478        // }
38479     },
38480
38481     /**
38482      * Sets the checked state of the checkbox.
38483      * On is always based on a string comparison between inputValue and the param.
38484      * @param {Boolean/String} value - the value to set 
38485      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38486      */
38487     setValue : function(v,suppressEvent){
38488         
38489         
38490         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38491         //if(this.el && this.el.dom){
38492         //    this.el.dom.checked = this.checked;
38493         //    this.el.dom.defaultChecked = this.checked;
38494         //}
38495         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38496         //this.fireEvent("check", this, this.checked);
38497     },
38498     // private..
38499     setChecked : function(state,suppressEvent)
38500     {
38501         if (this.inSetChecked) {
38502             this.checked = state;
38503             return;
38504         }
38505         
38506     
38507         if(this.wrap){
38508             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38509         }
38510         this.checked = state;
38511         if(suppressEvent !== true){
38512             this.fireEvent('check', this, state);
38513         }
38514         this.inSetChecked = true;
38515         this.el.dom.value = state ? this.inputValue : this.valueOff;
38516         this.inSetChecked = false;
38517         
38518     },
38519     // handle setting of hidden value by some other method!!?!?
38520     setFromHidden: function()
38521     {
38522         if(!this.el){
38523             return;
38524         }
38525         //console.log("SET FROM HIDDEN");
38526         //alert('setFrom hidden');
38527         this.setValue(this.el.dom.value);
38528     },
38529     
38530     onDestroy : function()
38531     {
38532         if(this.viewEl){
38533             Roo.get(this.viewEl).remove();
38534         }
38535          
38536         Roo.form.Checkbox.superclass.onDestroy.call(this);
38537     }
38538
38539 });/*
38540  * Based on:
38541  * Ext JS Library 1.1.1
38542  * Copyright(c) 2006-2007, Ext JS, LLC.
38543  *
38544  * Originally Released Under LGPL - original licence link has changed is not relivant.
38545  *
38546  * Fork - LGPL
38547  * <script type="text/javascript">
38548  */
38549  
38550 /**
38551  * @class Roo.form.Radio
38552  * @extends Roo.form.Checkbox
38553  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38554  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38555  * @constructor
38556  * Creates a new Radio
38557  * @param {Object} config Configuration options
38558  */
38559 Roo.form.Radio = function(){
38560     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38561 };
38562 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38563     inputType: 'radio',
38564
38565     /**
38566      * If this radio is part of a group, it will return the selected value
38567      * @return {String}
38568      */
38569     getGroupValue : function(){
38570         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38571     }
38572 });//<script type="text/javascript">
38573
38574 /*
38575  * Ext JS Library 1.1.1
38576  * Copyright(c) 2006-2007, Ext JS, LLC.
38577  * licensing@extjs.com
38578  * 
38579  * http://www.extjs.com/license
38580  */
38581  
38582  /*
38583   * 
38584   * Known bugs:
38585   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38586   * - IE ? - no idea how much works there.
38587   * 
38588   * 
38589   * 
38590   */
38591  
38592
38593 /**
38594  * @class Ext.form.HtmlEditor
38595  * @extends Ext.form.Field
38596  * Provides a lightweight HTML Editor component.
38597  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38598  * 
38599  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38600  * supported by this editor.</b><br/><br/>
38601  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38602  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38603  */
38604 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38605       /**
38606      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38607      */
38608     toolbars : false,
38609     /**
38610      * @cfg {String} createLinkText The default text for the create link prompt
38611      */
38612     createLinkText : 'Please enter the URL for the link:',
38613     /**
38614      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38615      */
38616     defaultLinkValue : 'http:/'+'/',
38617    
38618      /**
38619      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38620      *                        Roo.resizable.
38621      */
38622     resizable : false,
38623      /**
38624      * @cfg {Number} height (in pixels)
38625      */   
38626     height: 300,
38627    /**
38628      * @cfg {Number} width (in pixels)
38629      */   
38630     width: 500,
38631     
38632     /**
38633      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38634      * 
38635      */
38636     stylesheets: false,
38637     
38638     // id of frame..
38639     frameId: false,
38640     
38641     // private properties
38642     validationEvent : false,
38643     deferHeight: true,
38644     initialized : false,
38645     activated : false,
38646     sourceEditMode : false,
38647     onFocus : Roo.emptyFn,
38648     iframePad:3,
38649     hideMode:'offsets',
38650     
38651     defaultAutoCreate : { // modified by initCompnoent..
38652         tag: "textarea",
38653         style:"width:500px;height:300px;",
38654         autocomplete: "off"
38655     },
38656
38657     // private
38658     initComponent : function(){
38659         this.addEvents({
38660             /**
38661              * @event initialize
38662              * Fires when the editor is fully initialized (including the iframe)
38663              * @param {HtmlEditor} this
38664              */
38665             initialize: true,
38666             /**
38667              * @event activate
38668              * Fires when the editor is first receives the focus. Any insertion must wait
38669              * until after this event.
38670              * @param {HtmlEditor} this
38671              */
38672             activate: true,
38673              /**
38674              * @event beforesync
38675              * Fires before the textarea is updated with content from the editor iframe. Return false
38676              * to cancel the sync.
38677              * @param {HtmlEditor} this
38678              * @param {String} html
38679              */
38680             beforesync: true,
38681              /**
38682              * @event beforepush
38683              * Fires before the iframe editor is updated with content from the textarea. Return false
38684              * to cancel the push.
38685              * @param {HtmlEditor} this
38686              * @param {String} html
38687              */
38688             beforepush: true,
38689              /**
38690              * @event sync
38691              * Fires when the textarea is updated with content from the editor iframe.
38692              * @param {HtmlEditor} this
38693              * @param {String} html
38694              */
38695             sync: true,
38696              /**
38697              * @event push
38698              * Fires when the iframe editor is updated with content from the textarea.
38699              * @param {HtmlEditor} this
38700              * @param {String} html
38701              */
38702             push: true,
38703              /**
38704              * @event editmodechange
38705              * Fires when the editor switches edit modes
38706              * @param {HtmlEditor} this
38707              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38708              */
38709             editmodechange: true,
38710             /**
38711              * @event editorevent
38712              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38713              * @param {HtmlEditor} this
38714              */
38715             editorevent: true
38716         });
38717         this.defaultAutoCreate =  {
38718             tag: "textarea",
38719             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38720             autocomplete: "off"
38721         };
38722     },
38723
38724     /**
38725      * Protected method that will not generally be called directly. It
38726      * is called when the editor creates its toolbar. Override this method if you need to
38727      * add custom toolbar buttons.
38728      * @param {HtmlEditor} editor
38729      */
38730     createToolbar : function(editor){
38731         if (!editor.toolbars || !editor.toolbars.length) {
38732             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38733         }
38734         
38735         for (var i =0 ; i < editor.toolbars.length;i++) {
38736             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38737             editor.toolbars[i].init(editor);
38738         }
38739          
38740         
38741     },
38742
38743     /**
38744      * Protected method that will not generally be called directly. It
38745      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38746      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38747      */
38748     getDocMarkup : function(){
38749         // body styles..
38750         var st = '';
38751         if (this.stylesheets === false) {
38752             
38753             Roo.get(document.head).select('style').each(function(node) {
38754                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38755             });
38756             
38757             Roo.get(document.head).select('link').each(function(node) { 
38758                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38759             });
38760             
38761         } else if (!this.stylesheets.length) {
38762                 // simple..
38763                 st = '<style type="text/css">' +
38764                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38765                    '</style>';
38766         } else {
38767             Roo.each(this.stylesheets, function(s) {
38768                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38769             });
38770             
38771         }
38772         
38773         return '<html><head>' + st  +
38774             //<style type="text/css">' +
38775             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38776             //'</style>' +
38777             ' </head><body></body></html>';
38778     },
38779
38780     // private
38781     onRender : function(ct, position)
38782     {
38783         var _t = this;
38784         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38785         this.el.dom.style.border = '0 none';
38786         this.el.dom.setAttribute('tabIndex', -1);
38787         this.el.addClass('x-hidden');
38788         if(Roo.isIE){ // fix IE 1px bogus margin
38789             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38790         }
38791         this.wrap = this.el.wrap({
38792             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38793         });
38794         
38795         if (this.resizable) {
38796             this.resizeEl = new Roo.Resizable(this.wrap, {
38797                 pinned : true,
38798                 wrap: true,
38799                 dynamic : true,
38800                 minHeight : this.height,
38801                 height: this.height,
38802                 handles : this.resizable,
38803                 width: this.width,
38804                 listeners : {
38805                     resize : function(r, w, h) {
38806                         _t.onResize(w,h); // -something
38807                     }
38808                 }
38809             });
38810             
38811         }
38812
38813         this.frameId = Roo.id();
38814         
38815         this.createToolbar(this);
38816         
38817       
38818         
38819         var iframe = this.wrap.createChild({
38820             tag: 'iframe',
38821             id: this.frameId,
38822             name: this.frameId,
38823             frameBorder : 'no',
38824             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38825         }, this.el
38826         );
38827         
38828        // console.log(iframe);
38829         //this.wrap.dom.appendChild(iframe);
38830
38831         this.iframe = iframe.dom;
38832
38833          this.assignDocWin();
38834         
38835         this.doc.designMode = 'on';
38836        
38837         this.doc.open();
38838         this.doc.write(this.getDocMarkup());
38839         this.doc.close();
38840
38841         
38842         var task = { // must defer to wait for browser to be ready
38843             run : function(){
38844                 //console.log("run task?" + this.doc.readyState);
38845                 this.assignDocWin();
38846                 if(this.doc.body || this.doc.readyState == 'complete'){
38847                     try {
38848                         this.doc.designMode="on";
38849                     } catch (e) {
38850                         return;
38851                     }
38852                     Roo.TaskMgr.stop(task);
38853                     this.initEditor.defer(10, this);
38854                 }
38855             },
38856             interval : 10,
38857             duration:10000,
38858             scope: this
38859         };
38860         Roo.TaskMgr.start(task);
38861
38862         if(!this.width){
38863             this.setSize(this.wrap.getSize());
38864         }
38865         if (this.resizeEl) {
38866             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38867             // should trigger onReize..
38868         }
38869     },
38870
38871     // private
38872     onResize : function(w, h)
38873     {
38874         //Roo.log('resize: ' +w + ',' + h );
38875         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38876         if(this.el && this.iframe){
38877             if(typeof w == 'number'){
38878                 var aw = w - this.wrap.getFrameWidth('lr');
38879                 this.el.setWidth(this.adjustWidth('textarea', aw));
38880                 this.iframe.style.width = aw + 'px';
38881             }
38882             if(typeof h == 'number'){
38883                 var tbh = 0;
38884                 for (var i =0; i < this.toolbars.length;i++) {
38885                     // fixme - ask toolbars for heights?
38886                     tbh += this.toolbars[i].tb.el.getHeight();
38887                     if (this.toolbars[i].footer) {
38888                         tbh += this.toolbars[i].footer.el.getHeight();
38889                     }
38890                 }
38891                 
38892                 
38893                 
38894                 
38895                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38896                 ah -= 5; // knock a few pixes off for look..
38897                 this.el.setHeight(this.adjustWidth('textarea', ah));
38898                 this.iframe.style.height = ah + 'px';
38899                 if(this.doc){
38900                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38901                 }
38902             }
38903         }
38904     },
38905
38906     /**
38907      * Toggles the editor between standard and source edit mode.
38908      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38909      */
38910     toggleSourceEdit : function(sourceEditMode){
38911         
38912         this.sourceEditMode = sourceEditMode === true;
38913         
38914         if(this.sourceEditMode){
38915           
38916             this.syncValue();
38917             this.iframe.className = 'x-hidden';
38918             this.el.removeClass('x-hidden');
38919             this.el.dom.removeAttribute('tabIndex');
38920             this.el.focus();
38921         }else{
38922              
38923             this.pushValue();
38924             this.iframe.className = '';
38925             this.el.addClass('x-hidden');
38926             this.el.dom.setAttribute('tabIndex', -1);
38927             this.deferFocus();
38928         }
38929         this.setSize(this.wrap.getSize());
38930         this.fireEvent('editmodechange', this, this.sourceEditMode);
38931     },
38932
38933     // private used internally
38934     createLink : function(){
38935         var url = prompt(this.createLinkText, this.defaultLinkValue);
38936         if(url && url != 'http:/'+'/'){
38937             this.relayCmd('createlink', url);
38938         }
38939     },
38940
38941     // private (for BoxComponent)
38942     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38943
38944     // private (for BoxComponent)
38945     getResizeEl : function(){
38946         return this.wrap;
38947     },
38948
38949     // private (for BoxComponent)
38950     getPositionEl : function(){
38951         return this.wrap;
38952     },
38953
38954     // private
38955     initEvents : function(){
38956         this.originalValue = this.getValue();
38957     },
38958
38959     /**
38960      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38961      * @method
38962      */
38963     markInvalid : Roo.emptyFn,
38964     /**
38965      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38966      * @method
38967      */
38968     clearInvalid : Roo.emptyFn,
38969
38970     setValue : function(v){
38971         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38972         this.pushValue();
38973     },
38974
38975     /**
38976      * Protected method that will not generally be called directly. If you need/want
38977      * custom HTML cleanup, this is the method you should override.
38978      * @param {String} html The HTML to be cleaned
38979      * return {String} The cleaned HTML
38980      */
38981     cleanHtml : function(html){
38982         html = String(html);
38983         if(html.length > 5){
38984             if(Roo.isSafari){ // strip safari nonsense
38985                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38986             }
38987         }
38988         if(html == '&nbsp;'){
38989             html = '';
38990         }
38991         return html;
38992     },
38993
38994     /**
38995      * Protected method that will not generally be called directly. Syncs the contents
38996      * of the editor iframe with the textarea.
38997      */
38998     syncValue : function(){
38999         if(this.initialized){
39000             var bd = (this.doc.body || this.doc.documentElement);
39001             this.cleanUpPaste();
39002             var html = bd.innerHTML;
39003             if(Roo.isSafari){
39004                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39005                 var m = bs.match(/text-align:(.*?);/i);
39006                 if(m && m[1]){
39007                     html = '<div style="'+m[0]+'">' + html + '</div>';
39008                 }
39009             }
39010             html = this.cleanHtml(html);
39011             if(this.fireEvent('beforesync', this, html) !== false){
39012                 this.el.dom.value = html;
39013                 this.fireEvent('sync', this, html);
39014             }
39015         }
39016     },
39017
39018     /**
39019      * Protected method that will not generally be called directly. Pushes the value of the textarea
39020      * into the iframe editor.
39021      */
39022     pushValue : function(){
39023         if(this.initialized){
39024             var v = this.el.dom.value;
39025             if(v.length < 1){
39026                 v = '&#160;';
39027             }
39028             
39029             if(this.fireEvent('beforepush', this, v) !== false){
39030                 var d = (this.doc.body || this.doc.documentElement);
39031                 d.innerHTML = v;
39032                 this.cleanUpPaste();
39033                 this.el.dom.value = d.innerHTML;
39034                 this.fireEvent('push', this, v);
39035             }
39036         }
39037     },
39038
39039     // private
39040     deferFocus : function(){
39041         this.focus.defer(10, this);
39042     },
39043
39044     // doc'ed in Field
39045     focus : function(){
39046         if(this.win && !this.sourceEditMode){
39047             this.win.focus();
39048         }else{
39049             this.el.focus();
39050         }
39051     },
39052     
39053     assignDocWin: function()
39054     {
39055         var iframe = this.iframe;
39056         
39057          if(Roo.isIE){
39058             this.doc = iframe.contentWindow.document;
39059             this.win = iframe.contentWindow;
39060         } else {
39061             if (!Roo.get(this.frameId)) {
39062                 return;
39063             }
39064             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39065             this.win = Roo.get(this.frameId).dom.contentWindow;
39066         }
39067     },
39068     
39069     // private
39070     initEditor : function(){
39071         //console.log("INIT EDITOR");
39072         this.assignDocWin();
39073         
39074         
39075         
39076         this.doc.designMode="on";
39077         this.doc.open();
39078         this.doc.write(this.getDocMarkup());
39079         this.doc.close();
39080         
39081         var dbody = (this.doc.body || this.doc.documentElement);
39082         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39083         // this copies styles from the containing element into thsi one..
39084         // not sure why we need all of this..
39085         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39086         ss['background-attachment'] = 'fixed'; // w3c
39087         dbody.bgProperties = 'fixed'; // ie
39088         Roo.DomHelper.applyStyles(dbody, ss);
39089         Roo.EventManager.on(this.doc, {
39090             //'mousedown': this.onEditorEvent,
39091             'mouseup': this.onEditorEvent,
39092             'dblclick': this.onEditorEvent,
39093             'click': this.onEditorEvent,
39094             'keyup': this.onEditorEvent,
39095             buffer:100,
39096             scope: this
39097         });
39098         if(Roo.isGecko){
39099             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39100         }
39101         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39102             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39103         }
39104         this.initialized = true;
39105
39106         this.fireEvent('initialize', this);
39107         this.pushValue();
39108     },
39109
39110     // private
39111     onDestroy : function(){
39112         
39113         
39114         
39115         if(this.rendered){
39116             
39117             for (var i =0; i < this.toolbars.length;i++) {
39118                 // fixme - ask toolbars for heights?
39119                 this.toolbars[i].onDestroy();
39120             }
39121             
39122             this.wrap.dom.innerHTML = '';
39123             this.wrap.remove();
39124         }
39125     },
39126
39127     // private
39128     onFirstFocus : function(){
39129         
39130         this.assignDocWin();
39131         
39132         
39133         this.activated = true;
39134         for (var i =0; i < this.toolbars.length;i++) {
39135             this.toolbars[i].onFirstFocus();
39136         }
39137        
39138         if(Roo.isGecko){ // prevent silly gecko errors
39139             this.win.focus();
39140             var s = this.win.getSelection();
39141             if(!s.focusNode || s.focusNode.nodeType != 3){
39142                 var r = s.getRangeAt(0);
39143                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39144                 r.collapse(true);
39145                 this.deferFocus();
39146             }
39147             try{
39148                 this.execCmd('useCSS', true);
39149                 this.execCmd('styleWithCSS', false);
39150             }catch(e){}
39151         }
39152         this.fireEvent('activate', this);
39153     },
39154
39155     // private
39156     adjustFont: function(btn){
39157         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39158         //if(Roo.isSafari){ // safari
39159         //    adjust *= 2;
39160        // }
39161         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39162         if(Roo.isSafari){ // safari
39163             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39164             v =  (v < 10) ? 10 : v;
39165             v =  (v > 48) ? 48 : v;
39166             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39167             
39168         }
39169         
39170         
39171         v = Math.max(1, v+adjust);
39172         
39173         this.execCmd('FontSize', v  );
39174     },
39175
39176     onEditorEvent : function(e){
39177         this.fireEvent('editorevent', this, e);
39178       //  this.updateToolbar();
39179         this.syncValue();
39180     },
39181
39182     insertTag : function(tg)
39183     {
39184         // could be a bit smarter... -> wrap the current selected tRoo..
39185         
39186         this.execCmd("formatblock",   tg);
39187         
39188     },
39189     
39190     insertText : function(txt)
39191     {
39192         
39193         
39194         range = this.createRange();
39195         range.deleteContents();
39196                //alert(Sender.getAttribute('label'));
39197                
39198         range.insertNode(this.doc.createTextNode(txt));
39199     } ,
39200     
39201     // private
39202     relayBtnCmd : function(btn){
39203         this.relayCmd(btn.cmd);
39204     },
39205
39206     /**
39207      * Executes a Midas editor command on the editor document and performs necessary focus and
39208      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39209      * @param {String} cmd The Midas command
39210      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39211      */
39212     relayCmd : function(cmd, value){
39213         this.win.focus();
39214         this.execCmd(cmd, value);
39215         this.fireEvent('editorevent', this);
39216         //this.updateToolbar();
39217         this.deferFocus();
39218     },
39219
39220     /**
39221      * Executes a Midas editor command directly on the editor document.
39222      * For visual commands, you should use {@link #relayCmd} instead.
39223      * <b>This should only be called after the editor is initialized.</b>
39224      * @param {String} cmd The Midas command
39225      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39226      */
39227     execCmd : function(cmd, value){
39228         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39229         this.syncValue();
39230     },
39231
39232    
39233     /**
39234      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39235      * to insert tRoo.
39236      * @param {String} text
39237      */
39238     insertAtCursor : function(text){
39239         if(!this.activated){
39240             return;
39241         }
39242         if(Roo.isIE){
39243             this.win.focus();
39244             var r = this.doc.selection.createRange();
39245             if(r){
39246                 r.collapse(true);
39247                 r.pasteHTML(text);
39248                 this.syncValue();
39249                 this.deferFocus();
39250             }
39251         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39252             this.win.focus();
39253             this.execCmd('InsertHTML', text);
39254             this.deferFocus();
39255         }
39256     },
39257  // private
39258     mozKeyPress : function(e){
39259         if(e.ctrlKey){
39260             var c = e.getCharCode(), cmd;
39261           
39262             if(c > 0){
39263                 c = String.fromCharCode(c).toLowerCase();
39264                 switch(c){
39265                     case 'b':
39266                         cmd = 'bold';
39267                     break;
39268                     case 'i':
39269                         cmd = 'italic';
39270                     break;
39271                     case 'u':
39272                         cmd = 'underline';
39273                     case 'v':
39274                         this.cleanUpPaste.defer(100, this);
39275                         return;
39276                     break;
39277                 }
39278                 if(cmd){
39279                     this.win.focus();
39280                     this.execCmd(cmd);
39281                     this.deferFocus();
39282                     e.preventDefault();
39283                 }
39284                 
39285             }
39286         }
39287     },
39288
39289     // private
39290     fixKeys : function(){ // load time branching for fastest keydown performance
39291         if(Roo.isIE){
39292             return function(e){
39293                 var k = e.getKey(), r;
39294                 if(k == e.TAB){
39295                     e.stopEvent();
39296                     r = this.doc.selection.createRange();
39297                     if(r){
39298                         r.collapse(true);
39299                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39300                         this.deferFocus();
39301                     }
39302                     return;
39303                 }
39304                 
39305                 if(k == e.ENTER){
39306                     r = this.doc.selection.createRange();
39307                     if(r){
39308                         var target = r.parentElement();
39309                         if(!target || target.tagName.toLowerCase() != 'li'){
39310                             e.stopEvent();
39311                             r.pasteHTML('<br />');
39312                             r.collapse(false);
39313                             r.select();
39314                         }
39315                     }
39316                 }
39317                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39318                     this.cleanUpPaste.defer(100, this);
39319                     return;
39320                 }
39321                 
39322                 
39323             };
39324         }else if(Roo.isOpera){
39325             return function(e){
39326                 var k = e.getKey();
39327                 if(k == e.TAB){
39328                     e.stopEvent();
39329                     this.win.focus();
39330                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39331                     this.deferFocus();
39332                 }
39333                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39334                     this.cleanUpPaste.defer(100, this);
39335                     return;
39336                 }
39337                 
39338             };
39339         }else if(Roo.isSafari){
39340             return function(e){
39341                 var k = e.getKey();
39342                 
39343                 if(k == e.TAB){
39344                     e.stopEvent();
39345                     this.execCmd('InsertText','\t');
39346                     this.deferFocus();
39347                     return;
39348                 }
39349                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39350                     this.cleanUpPaste.defer(100, this);
39351                     return;
39352                 }
39353                 
39354              };
39355         }
39356     }(),
39357     
39358     getAllAncestors: function()
39359     {
39360         var p = this.getSelectedNode();
39361         var a = [];
39362         if (!p) {
39363             a.push(p); // push blank onto stack..
39364             p = this.getParentElement();
39365         }
39366         
39367         
39368         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39369             a.push(p);
39370             p = p.parentNode;
39371         }
39372         a.push(this.doc.body);
39373         return a;
39374     },
39375     lastSel : false,
39376     lastSelNode : false,
39377     
39378     
39379     getSelection : function() 
39380     {
39381         this.assignDocWin();
39382         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39383     },
39384     
39385     getSelectedNode: function() 
39386     {
39387         // this may only work on Gecko!!!
39388         
39389         // should we cache this!!!!
39390         
39391         
39392         
39393          
39394         var range = this.createRange(this.getSelection()).cloneRange();
39395         
39396         if (Roo.isIE) {
39397             var parent = range.parentElement();
39398             while (true) {
39399                 var testRange = range.duplicate();
39400                 testRange.moveToElementText(parent);
39401                 if (testRange.inRange(range)) {
39402                     break;
39403                 }
39404                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39405                     break;
39406                 }
39407                 parent = parent.parentElement;
39408             }
39409             return parent;
39410         }
39411         
39412         // is ancestor a text element.
39413         var ac =  range.commonAncestorContainer;
39414         if (ac.nodeType == 3) {
39415             ac = ac.parentNode;
39416         }
39417         
39418         var ar = ac.childNodes;
39419          
39420         var nodes = [];
39421         var other_nodes = [];
39422         var has_other_nodes = false;
39423         for (var i=0;i<ar.length;i++) {
39424             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39425                 continue;
39426             }
39427             // fullly contained node.
39428             
39429             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39430                 nodes.push(ar[i]);
39431                 continue;
39432             }
39433             
39434             // probably selected..
39435             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39436                 other_nodes.push(ar[i]);
39437                 continue;
39438             }
39439             // outer..
39440             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39441                 continue;
39442             }
39443             
39444             
39445             has_other_nodes = true;
39446         }
39447         if (!nodes.length && other_nodes.length) {
39448             nodes= other_nodes;
39449         }
39450         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39451             return false;
39452         }
39453         
39454         return nodes[0];
39455     },
39456     createRange: function(sel)
39457     {
39458         // this has strange effects when using with 
39459         // top toolbar - not sure if it's a great idea.
39460         //this.editor.contentWindow.focus();
39461         if (typeof sel != "undefined") {
39462             try {
39463                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39464             } catch(e) {
39465                 return this.doc.createRange();
39466             }
39467         } else {
39468             return this.doc.createRange();
39469         }
39470     },
39471     getParentElement: function()
39472     {
39473         
39474         this.assignDocWin();
39475         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39476         
39477         var range = this.createRange(sel);
39478          
39479         try {
39480             var p = range.commonAncestorContainer;
39481             while (p.nodeType == 3) { // text node
39482                 p = p.parentNode;
39483             }
39484             return p;
39485         } catch (e) {
39486             return null;
39487         }
39488     
39489     },
39490     /***
39491      *
39492      * Range intersection.. the hard stuff...
39493      *  '-1' = before
39494      *  '0' = hits..
39495      *  '1' = after.
39496      *         [ -- selected range --- ]
39497      *   [fail]                        [fail]
39498      *
39499      *    basically..
39500      *      if end is before start or  hits it. fail.
39501      *      if start is after end or hits it fail.
39502      *
39503      *   if either hits (but other is outside. - then it's not 
39504      *   
39505      *    
39506      **/
39507     
39508     
39509     // @see http://www.thismuchiknow.co.uk/?p=64.
39510     rangeIntersectsNode : function(range, node)
39511     {
39512         var nodeRange = node.ownerDocument.createRange();
39513         try {
39514             nodeRange.selectNode(node);
39515         } catch (e) {
39516             nodeRange.selectNodeContents(node);
39517         }
39518     
39519         var rangeStartRange = range.cloneRange();
39520         rangeStartRange.collapse(true);
39521     
39522         var rangeEndRange = range.cloneRange();
39523         rangeEndRange.collapse(false);
39524     
39525         var nodeStartRange = nodeRange.cloneRange();
39526         nodeStartRange.collapse(true);
39527     
39528         var nodeEndRange = nodeRange.cloneRange();
39529         nodeEndRange.collapse(false);
39530     
39531         return rangeStartRange.compareBoundaryPoints(
39532                  Range.START_TO_START, nodeEndRange) == -1 &&
39533                rangeEndRange.compareBoundaryPoints(
39534                  Range.START_TO_START, nodeStartRange) == 1;
39535         
39536          
39537     },
39538     rangeCompareNode : function(range, node)
39539     {
39540         var nodeRange = node.ownerDocument.createRange();
39541         try {
39542             nodeRange.selectNode(node);
39543         } catch (e) {
39544             nodeRange.selectNodeContents(node);
39545         }
39546         
39547         
39548         range.collapse(true);
39549     
39550         nodeRange.collapse(true);
39551      
39552         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39553         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39554          
39555         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39556         
39557         var nodeIsBefore   =  ss == 1;
39558         var nodeIsAfter    = ee == -1;
39559         
39560         if (nodeIsBefore && nodeIsAfter)
39561             return 0; // outer
39562         if (!nodeIsBefore && nodeIsAfter)
39563             return 1; //right trailed.
39564         
39565         if (nodeIsBefore && !nodeIsAfter)
39566             return 2;  // left trailed.
39567         // fully contined.
39568         return 3;
39569     },
39570
39571     // private? - in a new class?
39572     cleanUpPaste :  function()
39573     {
39574         // cleans up the whole document..
39575       //  console.log('cleanuppaste');
39576         this.cleanUpChildren(this.doc.body);
39577         
39578         
39579     },
39580     cleanUpChildren : function (n)
39581     {
39582         if (!n.childNodes.length) {
39583             return;
39584         }
39585         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39586            this.cleanUpChild(n.childNodes[i]);
39587         }
39588     },
39589     
39590     
39591         
39592     
39593     cleanUpChild : function (node)
39594     {
39595         //console.log(node);
39596         if (node.nodeName == "#text") {
39597             // clean up silly Windows -- stuff?
39598             return; 
39599         }
39600         if (node.nodeName == "#comment") {
39601             node.parentNode.removeChild(node);
39602             // clean up silly Windows -- stuff?
39603             return; 
39604         }
39605         
39606         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39607             // remove node.
39608             node.parentNode.removeChild(node);
39609             return;
39610             
39611         }
39612         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39613             this.cleanUpChildren(node);
39614             // inserts everything just before this node...
39615             while (node.childNodes.length) {
39616                 var cn = node.childNodes[0];
39617                 node.removeChild(cn);
39618                 node.parentNode.insertBefore(cn, node);
39619             }
39620             node.parentNode.removeChild(node);
39621             return;
39622         }
39623         
39624         if (!node.attributes || !node.attributes.length) {
39625             this.cleanUpChildren(node);
39626             return;
39627         }
39628         
39629         function cleanAttr(n,v)
39630         {
39631             
39632             if (v.match(/^\./) || v.match(/^\//)) {
39633                 return;
39634             }
39635             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39636                 return;
39637             }
39638             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39639             node.removeAttribute(n);
39640             
39641         }
39642         
39643         function cleanStyle(n,v)
39644         {
39645             if (v.match(/expression/)) { //XSS?? should we even bother..
39646                 node.removeAttribute(n);
39647                 return;
39648             }
39649             
39650             
39651             var parts = v.split(/;/);
39652             Roo.each(parts, function(p) {
39653                 p = p.replace(/\s+/g,'');
39654                 if (!p.length) {
39655                     return true;
39656                 }
39657                 var l = p.split(':').shift().replace(/\s+/g,'');
39658                 
39659                 // only allow 'c whitelisted system attributes'
39660                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39661                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39662                     node.removeAttribute(n);
39663                     return false;
39664                 }
39665                 return true;
39666             });
39667             
39668             
39669         }
39670         
39671         
39672         for (var i = node.attributes.length-1; i > -1 ; i--) {
39673             var a = node.attributes[i];
39674             //console.log(a);
39675             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39676                 node.removeAttribute(a.name);
39677                 return;
39678             }
39679             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39680                 cleanAttr(a.name,a.value); // fixme..
39681                 return;
39682             }
39683             if (a.name == 'style') {
39684                 cleanStyle(a.name,a.value);
39685             }
39686             /// clean up MS crap..
39687             if (a.name == 'class') {
39688                 if (a.value.match(/^Mso/)) {
39689                     node.className = '';
39690                 }
39691             }
39692             
39693             // style cleanup!?
39694             // class cleanup?
39695             
39696         }
39697         
39698         
39699         this.cleanUpChildren(node);
39700         
39701         
39702     }
39703     
39704     
39705     // hide stuff that is not compatible
39706     /**
39707      * @event blur
39708      * @hide
39709      */
39710     /**
39711      * @event change
39712      * @hide
39713      */
39714     /**
39715      * @event focus
39716      * @hide
39717      */
39718     /**
39719      * @event specialkey
39720      * @hide
39721      */
39722     /**
39723      * @cfg {String} fieldClass @hide
39724      */
39725     /**
39726      * @cfg {String} focusClass @hide
39727      */
39728     /**
39729      * @cfg {String} autoCreate @hide
39730      */
39731     /**
39732      * @cfg {String} inputType @hide
39733      */
39734     /**
39735      * @cfg {String} invalidClass @hide
39736      */
39737     /**
39738      * @cfg {String} invalidText @hide
39739      */
39740     /**
39741      * @cfg {String} msgFx @hide
39742      */
39743     /**
39744      * @cfg {String} validateOnBlur @hide
39745      */
39746 });
39747
39748 Roo.form.HtmlEditor.white = [
39749         'area', 'br', 'img', 'input', 'hr', 'wbr',
39750         
39751        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39752        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39753        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39754        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39755        'table',   'ul',         'xmp', 
39756        
39757        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39758       'thead',   'tr', 
39759      
39760       'dir', 'menu', 'ol', 'ul', 'dl',
39761        
39762       'embed',  'object'
39763 ];
39764
39765
39766 Roo.form.HtmlEditor.black = [
39767     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39768         'applet', // 
39769         'base',   'basefont', 'bgsound', 'blink',  'body', 
39770         'frame',  'frameset', 'head',    'html',   'ilayer', 
39771         'iframe', 'layer',  'link',     'meta',    'object',   
39772         'script', 'style' ,'title',  'xml' // clean later..
39773 ];
39774 Roo.form.HtmlEditor.clean = [
39775     'script', 'style', 'title', 'xml'
39776 ];
39777 Roo.form.HtmlEditor.remove = [
39778     'font'
39779 ];
39780 // attributes..
39781
39782 Roo.form.HtmlEditor.ablack = [
39783     'on'
39784 ];
39785     
39786 Roo.form.HtmlEditor.aclean = [ 
39787     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39788 ];
39789
39790 // protocols..
39791 Roo.form.HtmlEditor.pwhite= [
39792         'http',  'https',  'mailto'
39793 ];
39794
39795 // white listed style attributes.
39796 Roo.form.HtmlEditor.cwhite= [
39797         'text-align',
39798         'font-size'
39799 ];
39800
39801 // <script type="text/javascript">
39802 /*
39803  * Based on
39804  * Ext JS Library 1.1.1
39805  * Copyright(c) 2006-2007, Ext JS, LLC.
39806  *  
39807  
39808  */
39809
39810 /**
39811  * @class Roo.form.HtmlEditorToolbar1
39812  * Basic Toolbar
39813  * 
39814  * Usage:
39815  *
39816  new Roo.form.HtmlEditor({
39817     ....
39818     toolbars : [
39819         new Roo.form.HtmlEditorToolbar1({
39820             disable : { fonts: 1 , format: 1, ..., ... , ...],
39821             btns : [ .... ]
39822         })
39823     }
39824      
39825  * 
39826  * @cfg {Object} disable List of elements to disable..
39827  * @cfg {Array} btns List of additional buttons.
39828  * 
39829  * 
39830  * NEEDS Extra CSS? 
39831  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39832  */
39833  
39834 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39835 {
39836     
39837     Roo.apply(this, config);
39838     
39839     // default disabled, based on 'good practice'..
39840     this.disable = this.disable || {};
39841     Roo.applyIf(this.disable, {
39842         fontSize : true,
39843         colors : true,
39844         specialElements : true
39845     });
39846     
39847     
39848     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39849     // dont call parent... till later.
39850 }
39851
39852 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39853     
39854     tb: false,
39855     
39856     rendered: false,
39857     
39858     editor : false,
39859     /**
39860      * @cfg {Object} disable  List of toolbar elements to disable
39861          
39862      */
39863     disable : false,
39864       /**
39865      * @cfg {Array} fontFamilies An array of available font families
39866      */
39867     fontFamilies : [
39868         'Arial',
39869         'Courier New',
39870         'Tahoma',
39871         'Times New Roman',
39872         'Verdana'
39873     ],
39874     
39875     specialChars : [
39876            "&#169;",
39877           "&#174;",     
39878           "&#8482;",    
39879           "&#163;" ,    
39880          // "&#8212;",    
39881           "&#8230;",    
39882           "&#247;" ,    
39883         //  "&#225;" ,     ?? a acute?
39884            "&#8364;"    , //Euro
39885        //   "&#8220;"    ,
39886         //  "&#8221;"    ,
39887         //  "&#8226;"    ,
39888           "&#176;"  //   , // degrees
39889
39890          // "&#233;"     , // e ecute
39891          // "&#250;"     , // u ecute?
39892     ],
39893     
39894     specialElements : [
39895         {
39896             text: "Insert Table",
39897             xtype: 'MenuItem',
39898             xns : Roo.Menu,
39899             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39900                 
39901         },
39902         {    
39903             text: "Insert Image",
39904             xtype: 'MenuItem',
39905             xns : Roo.Menu,
39906             ihtml : '<img src="about:blank"/>'
39907             
39908         }
39909         
39910          
39911     ],
39912     
39913     
39914     inputElements : [ 
39915             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39916             "input:submit", "input:button", "select", "textarea", "label" ],
39917     formats : [
39918         ["p"] ,  
39919         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39920         ["pre"],[ "code"], 
39921         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39922     ],
39923      /**
39924      * @cfg {String} defaultFont default font to use.
39925      */
39926     defaultFont: 'tahoma',
39927    
39928     fontSelect : false,
39929     
39930     
39931     formatCombo : false,
39932     
39933     init : function(editor)
39934     {
39935         this.editor = editor;
39936         
39937         
39938         var fid = editor.frameId;
39939         var etb = this;
39940         function btn(id, toggle, handler){
39941             var xid = fid + '-'+ id ;
39942             return {
39943                 id : xid,
39944                 cmd : id,
39945                 cls : 'x-btn-icon x-edit-'+id,
39946                 enableToggle:toggle !== false,
39947                 scope: editor, // was editor...
39948                 handler:handler||editor.relayBtnCmd,
39949                 clickEvent:'mousedown',
39950                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39951                 tabIndex:-1
39952             };
39953         }
39954         
39955         
39956         
39957         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39958         this.tb = tb;
39959          // stop form submits
39960         tb.el.on('click', function(e){
39961             e.preventDefault(); // what does this do?
39962         });
39963
39964         if(!this.disable.font && !Roo.isSafari){
39965             /* why no safari for fonts
39966             editor.fontSelect = tb.el.createChild({
39967                 tag:'select',
39968                 tabIndex: -1,
39969                 cls:'x-font-select',
39970                 html: editor.createFontOptions()
39971             });
39972             editor.fontSelect.on('change', function(){
39973                 var font = editor.fontSelect.dom.value;
39974                 editor.relayCmd('fontname', font);
39975                 editor.deferFocus();
39976             }, editor);
39977             tb.add(
39978                 editor.fontSelect.dom,
39979                 '-'
39980             );
39981             */
39982         };
39983         if(!this.disable.formats){
39984             this.formatCombo = new Roo.form.ComboBox({
39985                 store: new Roo.data.SimpleStore({
39986                     id : 'tag',
39987                     fields: ['tag'],
39988                     data : this.formats // from states.js
39989                 }),
39990                 blockFocus : true,
39991                 //autoCreate : {tag: "div",  size: "20"},
39992                 displayField:'tag',
39993                 typeAhead: false,
39994                 mode: 'local',
39995                 editable : false,
39996                 triggerAction: 'all',
39997                 emptyText:'Add tag',
39998                 selectOnFocus:true,
39999                 width:135,
40000                 listeners : {
40001                     'select': function(c, r, i) {
40002                         editor.insertTag(r.get('tag'));
40003                         editor.focus();
40004                     }
40005                 }
40006
40007             });
40008             tb.addField(this.formatCombo);
40009             
40010         }
40011         
40012         if(!this.disable.format){
40013             tb.add(
40014                 btn('bold'),
40015                 btn('italic'),
40016                 btn('underline')
40017             );
40018         };
40019         if(!this.disable.fontSize){
40020             tb.add(
40021                 '-',
40022                 
40023                 
40024                 btn('increasefontsize', false, editor.adjustFont),
40025                 btn('decreasefontsize', false, editor.adjustFont)
40026             );
40027         };
40028         
40029         
40030         if(!this.disable.colors){
40031             tb.add(
40032                 '-', {
40033                     id:editor.frameId +'-forecolor',
40034                     cls:'x-btn-icon x-edit-forecolor',
40035                     clickEvent:'mousedown',
40036                     tooltip: this.buttonTips['forecolor'] || undefined,
40037                     tabIndex:-1,
40038                     menu : new Roo.menu.ColorMenu({
40039                         allowReselect: true,
40040                         focus: Roo.emptyFn,
40041                         value:'000000',
40042                         plain:true,
40043                         selectHandler: function(cp, color){
40044                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40045                             editor.deferFocus();
40046                         },
40047                         scope: editor,
40048                         clickEvent:'mousedown'
40049                     })
40050                 }, {
40051                     id:editor.frameId +'backcolor',
40052                     cls:'x-btn-icon x-edit-backcolor',
40053                     clickEvent:'mousedown',
40054                     tooltip: this.buttonTips['backcolor'] || undefined,
40055                     tabIndex:-1,
40056                     menu : new Roo.menu.ColorMenu({
40057                         focus: Roo.emptyFn,
40058                         value:'FFFFFF',
40059                         plain:true,
40060                         allowReselect: true,
40061                         selectHandler: function(cp, color){
40062                             if(Roo.isGecko){
40063                                 editor.execCmd('useCSS', false);
40064                                 editor.execCmd('hilitecolor', color);
40065                                 editor.execCmd('useCSS', true);
40066                                 editor.deferFocus();
40067                             }else{
40068                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40069                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40070                                 editor.deferFocus();
40071                             }
40072                         },
40073                         scope:editor,
40074                         clickEvent:'mousedown'
40075                     })
40076                 }
40077             );
40078         };
40079         // now add all the items...
40080         
40081
40082         if(!this.disable.alignments){
40083             tb.add(
40084                 '-',
40085                 btn('justifyleft'),
40086                 btn('justifycenter'),
40087                 btn('justifyright')
40088             );
40089         };
40090
40091         //if(!Roo.isSafari){
40092             if(!this.disable.links){
40093                 tb.add(
40094                     '-',
40095                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40096                 );
40097             };
40098
40099             if(!this.disable.lists){
40100                 tb.add(
40101                     '-',
40102                     btn('insertorderedlist'),
40103                     btn('insertunorderedlist')
40104                 );
40105             }
40106             if(!this.disable.sourceEdit){
40107                 tb.add(
40108                     '-',
40109                     btn('sourceedit', true, function(btn){
40110                         this.toggleSourceEdit(btn.pressed);
40111                     })
40112                 );
40113             }
40114         //}
40115         
40116         var smenu = { };
40117         // special menu.. - needs to be tidied up..
40118         if (!this.disable.special) {
40119             smenu = {
40120                 text: "&#169;",
40121                 cls: 'x-edit-none',
40122                 
40123                 menu : {
40124                     items : []
40125                 }
40126             };
40127             for (var i =0; i < this.specialChars.length; i++) {
40128                 smenu.menu.items.push({
40129                     
40130                     html: this.specialChars[i],
40131                     handler: function(a,b) {
40132                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40133                         
40134                     },
40135                     tabIndex:-1
40136                 });
40137             }
40138             
40139             
40140             tb.add(smenu);
40141             
40142             
40143         }
40144          
40145         if (!this.disable.specialElements) {
40146             var semenu = {
40147                 text: "Other;",
40148                 cls: 'x-edit-none',
40149                 menu : {
40150                     items : []
40151                 }
40152             };
40153             for (var i =0; i < this.specialElements.length; i++) {
40154                 semenu.menu.items.push(
40155                     Roo.apply({ 
40156                         handler: function(a,b) {
40157                             editor.insertAtCursor(this.ihtml);
40158                         }
40159                     }, this.specialElements[i])
40160                 );
40161                     
40162             }
40163             
40164             tb.add(semenu);
40165             
40166             
40167         }
40168          
40169         
40170         if (this.btns) {
40171             for(var i =0; i< this.btns.length;i++) {
40172                 var b = this.btns[i];
40173                 b.cls =  'x-edit-none';
40174                 b.scope = editor;
40175                 tb.add(b);
40176             }
40177         
40178         }
40179         
40180         
40181         
40182         // disable everything...
40183         
40184         this.tb.items.each(function(item){
40185            if(item.id != editor.frameId+ '-sourceedit'){
40186                 item.disable();
40187             }
40188         });
40189         this.rendered = true;
40190         
40191         // the all the btns;
40192         editor.on('editorevent', this.updateToolbar, this);
40193         // other toolbars need to implement this..
40194         //editor.on('editmodechange', this.updateToolbar, this);
40195     },
40196     
40197     
40198     
40199     /**
40200      * Protected method that will not generally be called directly. It triggers
40201      * a toolbar update by reading the markup state of the current selection in the editor.
40202      */
40203     updateToolbar: function(){
40204
40205         if(!this.editor.activated){
40206             this.editor.onFirstFocus();
40207             return;
40208         }
40209
40210         var btns = this.tb.items.map, 
40211             doc = this.editor.doc,
40212             frameId = this.editor.frameId;
40213
40214         if(!this.disable.font && !Roo.isSafari){
40215             /*
40216             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40217             if(name != this.fontSelect.dom.value){
40218                 this.fontSelect.dom.value = name;
40219             }
40220             */
40221         }
40222         if(!this.disable.format){
40223             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40224             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40225             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40226         }
40227         if(!this.disable.alignments){
40228             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40229             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40230             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40231         }
40232         if(!Roo.isSafari && !this.disable.lists){
40233             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40234             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40235         }
40236         
40237         var ans = this.editor.getAllAncestors();
40238         if (this.formatCombo) {
40239             
40240             
40241             var store = this.formatCombo.store;
40242             this.formatCombo.setValue("");
40243             for (var i =0; i < ans.length;i++) {
40244                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40245                     // select it..
40246                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40247                     break;
40248                 }
40249             }
40250         }
40251         
40252         
40253         
40254         // hides menus... - so this cant be on a menu...
40255         Roo.menu.MenuMgr.hideAll();
40256
40257         //this.editorsyncValue();
40258     },
40259    
40260     
40261     createFontOptions : function(){
40262         var buf = [], fs = this.fontFamilies, ff, lc;
40263         for(var i = 0, len = fs.length; i< len; i++){
40264             ff = fs[i];
40265             lc = ff.toLowerCase();
40266             buf.push(
40267                 '<option value="',lc,'" style="font-family:',ff,';"',
40268                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40269                     ff,
40270                 '</option>'
40271             );
40272         }
40273         return buf.join('');
40274     },
40275     
40276     toggleSourceEdit : function(sourceEditMode){
40277         if(sourceEditMode === undefined){
40278             sourceEditMode = !this.sourceEditMode;
40279         }
40280         this.sourceEditMode = sourceEditMode === true;
40281         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40282         // just toggle the button?
40283         if(btn.pressed !== this.editor.sourceEditMode){
40284             btn.toggle(this.editor.sourceEditMode);
40285             return;
40286         }
40287         
40288         if(this.sourceEditMode){
40289             this.tb.items.each(function(item){
40290                 if(item.cmd != 'sourceedit'){
40291                     item.disable();
40292                 }
40293             });
40294           
40295         }else{
40296             if(this.initialized){
40297                 this.tb.items.each(function(item){
40298                     item.enable();
40299                 });
40300             }
40301             
40302         }
40303         // tell the editor that it's been pressed..
40304         this.editor.toggleSourceEdit(sourceEditMode);
40305        
40306     },
40307      /**
40308      * Object collection of toolbar tooltips for the buttons in the editor. The key
40309      * is the command id associated with that button and the value is a valid QuickTips object.
40310      * For example:
40311 <pre><code>
40312 {
40313     bold : {
40314         title: 'Bold (Ctrl+B)',
40315         text: 'Make the selected text bold.',
40316         cls: 'x-html-editor-tip'
40317     },
40318     italic : {
40319         title: 'Italic (Ctrl+I)',
40320         text: 'Make the selected text italic.',
40321         cls: 'x-html-editor-tip'
40322     },
40323     ...
40324 </code></pre>
40325     * @type Object
40326      */
40327     buttonTips : {
40328         bold : {
40329             title: 'Bold (Ctrl+B)',
40330             text: 'Make the selected text bold.',
40331             cls: 'x-html-editor-tip'
40332         },
40333         italic : {
40334             title: 'Italic (Ctrl+I)',
40335             text: 'Make the selected text italic.',
40336             cls: 'x-html-editor-tip'
40337         },
40338         underline : {
40339             title: 'Underline (Ctrl+U)',
40340             text: 'Underline the selected text.',
40341             cls: 'x-html-editor-tip'
40342         },
40343         increasefontsize : {
40344             title: 'Grow Text',
40345             text: 'Increase the font size.',
40346             cls: 'x-html-editor-tip'
40347         },
40348         decreasefontsize : {
40349             title: 'Shrink Text',
40350             text: 'Decrease the font size.',
40351             cls: 'x-html-editor-tip'
40352         },
40353         backcolor : {
40354             title: 'Text Highlight Color',
40355             text: 'Change the background color of the selected text.',
40356             cls: 'x-html-editor-tip'
40357         },
40358         forecolor : {
40359             title: 'Font Color',
40360             text: 'Change the color of the selected text.',
40361             cls: 'x-html-editor-tip'
40362         },
40363         justifyleft : {
40364             title: 'Align Text Left',
40365             text: 'Align text to the left.',
40366             cls: 'x-html-editor-tip'
40367         },
40368         justifycenter : {
40369             title: 'Center Text',
40370             text: 'Center text in the editor.',
40371             cls: 'x-html-editor-tip'
40372         },
40373         justifyright : {
40374             title: 'Align Text Right',
40375             text: 'Align text to the right.',
40376             cls: 'x-html-editor-tip'
40377         },
40378         insertunorderedlist : {
40379             title: 'Bullet List',
40380             text: 'Start a bulleted list.',
40381             cls: 'x-html-editor-tip'
40382         },
40383         insertorderedlist : {
40384             title: 'Numbered List',
40385             text: 'Start a numbered list.',
40386             cls: 'x-html-editor-tip'
40387         },
40388         createlink : {
40389             title: 'Hyperlink',
40390             text: 'Make the selected text a hyperlink.',
40391             cls: 'x-html-editor-tip'
40392         },
40393         sourceedit : {
40394             title: 'Source Edit',
40395             text: 'Switch to source editing mode.',
40396             cls: 'x-html-editor-tip'
40397         }
40398     },
40399     // private
40400     onDestroy : function(){
40401         if(this.rendered){
40402             
40403             this.tb.items.each(function(item){
40404                 if(item.menu){
40405                     item.menu.removeAll();
40406                     if(item.menu.el){
40407                         item.menu.el.destroy();
40408                     }
40409                 }
40410                 item.destroy();
40411             });
40412              
40413         }
40414     },
40415     onFirstFocus: function() {
40416         this.tb.items.each(function(item){
40417            item.enable();
40418         });
40419     }
40420 });
40421
40422
40423
40424
40425 // <script type="text/javascript">
40426 /*
40427  * Based on
40428  * Ext JS Library 1.1.1
40429  * Copyright(c) 2006-2007, Ext JS, LLC.
40430  *  
40431  
40432  */
40433
40434  
40435 /**
40436  * @class Roo.form.HtmlEditor.ToolbarContext
40437  * Context Toolbar
40438  * 
40439  * Usage:
40440  *
40441  new Roo.form.HtmlEditor({
40442     ....
40443     toolbars : [
40444         { xtype: 'ToolbarStandard', styles : {} }
40445         { xtype: 'ToolbarContext', disable : {} }
40446     ]
40447 })
40448
40449      
40450  * 
40451  * @config : {Object} disable List of elements to disable.. (not done yet.)
40452  * @config : {Object} styles  Map of styles available.
40453  * 
40454  */
40455
40456 Roo.form.HtmlEditor.ToolbarContext = function(config)
40457 {
40458     
40459     Roo.apply(this, config);
40460     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40461     // dont call parent... till later.
40462     this.styles = this.styles || {};
40463 }
40464 Roo.form.HtmlEditor.ToolbarContext.types = {
40465     'IMG' : {
40466         width : {
40467             title: "Width",
40468             width: 40
40469         },
40470         height:  {
40471             title: "Height",
40472             width: 40
40473         },
40474         align: {
40475             title: "Align",
40476             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40477             width : 80
40478             
40479         },
40480         border: {
40481             title: "Border",
40482             width: 40
40483         },
40484         alt: {
40485             title: "Alt",
40486             width: 120
40487         },
40488         src : {
40489             title: "Src",
40490             width: 220
40491         }
40492         
40493     },
40494     'A' : {
40495         name : {
40496             title: "Name",
40497             width: 50
40498         },
40499         href:  {
40500             title: "Href",
40501             width: 220
40502         } // border?
40503         
40504     },
40505     'TABLE' : {
40506         rows : {
40507             title: "Rows",
40508             width: 20
40509         },
40510         cols : {
40511             title: "Cols",
40512             width: 20
40513         },
40514         width : {
40515             title: "Width",
40516             width: 40
40517         },
40518         height : {
40519             title: "Height",
40520             width: 40
40521         },
40522         border : {
40523             title: "Border",
40524             width: 20
40525         }
40526     },
40527     'TD' : {
40528         width : {
40529             title: "Width",
40530             width: 40
40531         },
40532         height : {
40533             title: "Height",
40534             width: 40
40535         },   
40536         align: {
40537             title: "Align",
40538             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40539             width: 80
40540         },
40541         valign: {
40542             title: "Valign",
40543             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40544             width: 80
40545         },
40546         colspan: {
40547             title: "Colspan",
40548             width: 20
40549             
40550         }
40551     },
40552     'INPUT' : {
40553         name : {
40554             title: "name",
40555             width: 120
40556         },
40557         value : {
40558             title: "Value",
40559             width: 120
40560         },
40561         width : {
40562             title: "Width",
40563             width: 40
40564         }
40565     },
40566     'LABEL' : {
40567         'for' : {
40568             title: "For",
40569             width: 120
40570         }
40571     },
40572     'TEXTAREA' : {
40573           name : {
40574             title: "name",
40575             width: 120
40576         },
40577         rows : {
40578             title: "Rows",
40579             width: 20
40580         },
40581         cols : {
40582             title: "Cols",
40583             width: 20
40584         }
40585     },
40586     'SELECT' : {
40587         name : {
40588             title: "name",
40589             width: 120
40590         },
40591         selectoptions : {
40592             title: "Options",
40593             width: 200
40594         }
40595     },
40596     
40597     // should we really allow this??
40598     // should this just be 
40599     'BODY' : {
40600         title : {
40601             title: "title",
40602             width: 200,
40603             disabled : true
40604         }
40605     },
40606     '*' : {
40607         // empty..
40608     }
40609 };
40610
40611
40612
40613 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40614     
40615     tb: false,
40616     
40617     rendered: false,
40618     
40619     editor : false,
40620     /**
40621      * @cfg {Object} disable  List of toolbar elements to disable
40622          
40623      */
40624     disable : false,
40625     /**
40626      * @cfg {Object} styles List of styles 
40627      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40628      *
40629      * These must be defined in the page, so they get rendered correctly..
40630      * .headline { }
40631      * TD.underline { }
40632      * 
40633      */
40634     styles : false,
40635     
40636     
40637     
40638     toolbars : false,
40639     
40640     init : function(editor)
40641     {
40642         this.editor = editor;
40643         
40644         
40645         var fid = editor.frameId;
40646         var etb = this;
40647         function btn(id, toggle, handler){
40648             var xid = fid + '-'+ id ;
40649             return {
40650                 id : xid,
40651                 cmd : id,
40652                 cls : 'x-btn-icon x-edit-'+id,
40653                 enableToggle:toggle !== false,
40654                 scope: editor, // was editor...
40655                 handler:handler||editor.relayBtnCmd,
40656                 clickEvent:'mousedown',
40657                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40658                 tabIndex:-1
40659             };
40660         }
40661         // create a new element.
40662         var wdiv = editor.wrap.createChild({
40663                 tag: 'div'
40664             }, editor.wrap.dom.firstChild.nextSibling, true);
40665         
40666         // can we do this more than once??
40667         
40668          // stop form submits
40669       
40670  
40671         // disable everything...
40672         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40673         this.toolbars = {};
40674            
40675         for (var i in  ty) {
40676           
40677             this.toolbars[i] = this.buildToolbar(ty[i],i);
40678         }
40679         this.tb = this.toolbars.BODY;
40680         this.tb.el.show();
40681         this.buildFooter();
40682         this.footer.show();
40683          
40684         this.rendered = true;
40685         
40686         // the all the btns;
40687         editor.on('editorevent', this.updateToolbar, this);
40688         // other toolbars need to implement this..
40689         //editor.on('editmodechange', this.updateToolbar, this);
40690     },
40691     
40692     
40693     
40694     /**
40695      * Protected method that will not generally be called directly. It triggers
40696      * a toolbar update by reading the markup state of the current selection in the editor.
40697      */
40698     updateToolbar: function(ignore_a,ignore_b,sel){
40699
40700         
40701         if(!this.editor.activated){
40702              this.editor.onFirstFocus();
40703             return;
40704         }
40705         var updateFooter = sel ? false : true;
40706         
40707         
40708         var ans = this.editor.getAllAncestors();
40709         
40710         // pick
40711         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40712         
40713         if (!sel) { 
40714             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40715             sel = sel ? sel : this.editor.doc.body;
40716             sel = sel.tagName.length ? sel : this.editor.doc.body;
40717             
40718         }
40719         // pick a menu that exists..
40720         var tn = sel.tagName.toUpperCase();
40721         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40722         
40723         tn = sel.tagName.toUpperCase();
40724         
40725         var lastSel = this.tb.selectedNode
40726         
40727         this.tb.selectedNode = sel;
40728         
40729         // if current menu does not match..
40730         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40731                 
40732             this.tb.el.hide();
40733             ///console.log("show: " + tn);
40734             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40735             this.tb.el.show();
40736             // update name
40737             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40738             
40739             
40740             // update attributes
40741             if (this.tb.fields) {
40742                 this.tb.fields.each(function(e) {
40743                    e.setValue(sel.getAttribute(e.name));
40744                 });
40745             }
40746             
40747             // update styles
40748             var st = this.tb.fields.item(0);
40749             st.store.removeAll();
40750             var cn = sel.className.split(/\s+/);
40751             
40752             var avs = [];
40753             if (this.styles['*']) {
40754                 
40755                 Roo.each(this.styles['*'], function(v) {
40756                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40757                 });
40758             }
40759             if (this.styles[tn]) { 
40760                 Roo.each(this.styles[tn], function(v) {
40761                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40762                 });
40763             }
40764             
40765             st.store.loadData(avs);
40766             st.collapse();
40767             st.setValue(cn);
40768             
40769             // flag our selected Node.
40770             this.tb.selectedNode = sel;
40771            
40772            
40773             Roo.menu.MenuMgr.hideAll();
40774
40775         }
40776         
40777         if (!updateFooter) {
40778             return;
40779         }
40780         // update the footer
40781         //
40782         var html = '';
40783         
40784         this.footerEls = ans.reverse();
40785         Roo.each(this.footerEls, function(a,i) {
40786             if (!a) { return; }
40787             html += html.length ? ' &gt; '  :  '';
40788             
40789             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40790             
40791         });
40792        
40793         // 
40794         var sz = this.footDisp.up('td').getSize();
40795         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40796         this.footDisp.dom.style.marginLeft = '5px';
40797         
40798         this.footDisp.dom.style.overflow = 'hidden';
40799         
40800         this.footDisp.dom.innerHTML = html;
40801             
40802         //this.editorsyncValue();
40803     },
40804    
40805        
40806     // private
40807     onDestroy : function(){
40808         if(this.rendered){
40809             
40810             this.tb.items.each(function(item){
40811                 if(item.menu){
40812                     item.menu.removeAll();
40813                     if(item.menu.el){
40814                         item.menu.el.destroy();
40815                     }
40816                 }
40817                 item.destroy();
40818             });
40819              
40820         }
40821     },
40822     onFirstFocus: function() {
40823         // need to do this for all the toolbars..
40824         this.tb.items.each(function(item){
40825            item.enable();
40826         });
40827     },
40828     buildToolbar: function(tlist, nm)
40829     {
40830         var editor = this.editor;
40831          // create a new element.
40832         var wdiv = editor.wrap.createChild({
40833                 tag: 'div'
40834             }, editor.wrap.dom.firstChild.nextSibling, true);
40835         
40836        
40837         var tb = new Roo.Toolbar(wdiv);
40838         // add the name..
40839         
40840         tb.add(nm+ ":&nbsp;");
40841         
40842         // styles...
40843         if (this.styles) {
40844             
40845             // this needs a multi-select checkbox...
40846             tb.addField( new Roo.form.ComboBox({
40847                 store: new Roo.data.SimpleStore({
40848                     id : 'val',
40849                     fields: ['val', 'selected'],
40850                     data : [] 
40851                 }),
40852                 name : 'className',
40853                 displayField:'val',
40854                 typeAhead: false,
40855                 mode: 'local',
40856                 editable : false,
40857                 triggerAction: 'all',
40858                 emptyText:'Select Style',
40859                 selectOnFocus:true,
40860                 width: 130,
40861                 listeners : {
40862                     'select': function(c, r, i) {
40863                         // initial support only for on class per el..
40864                         tb.selectedNode.className =  r ? r.get('val') : '';
40865                     }
40866                 }
40867     
40868             }));
40869         }
40870             
40871         
40872         
40873         for (var i in tlist) {
40874             
40875             var item = tlist[i];
40876             tb.add(item.title + ":&nbsp;");
40877             
40878             
40879             
40880             
40881             if (item.opts) {
40882                 // opts == pulldown..
40883                 tb.addField( new Roo.form.ComboBox({
40884                     store: new Roo.data.SimpleStore({
40885                         id : 'val',
40886                         fields: ['val'],
40887                         data : item.opts  
40888                     }),
40889                     name : i,
40890                     displayField:'val',
40891                     typeAhead: false,
40892                     mode: 'local',
40893                     editable : false,
40894                     triggerAction: 'all',
40895                     emptyText:'Select',
40896                     selectOnFocus:true,
40897                     width: item.width ? item.width  : 130,
40898                     listeners : {
40899                         'select': function(c, r, i) {
40900                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40901                         }
40902                     }
40903
40904                 }));
40905                 continue;
40906                     
40907                  
40908                 
40909                 tb.addField( new Roo.form.TextField({
40910                     name: i,
40911                     width: 100,
40912                     //allowBlank:false,
40913                     value: ''
40914                 }));
40915                 continue;
40916             }
40917             tb.addField( new Roo.form.TextField({
40918                 name: i,
40919                 width: item.width,
40920                 //allowBlank:true,
40921                 value: '',
40922                 listeners: {
40923                     'change' : function(f, nv, ov) {
40924                         tb.selectedNode.setAttribute(f.name, nv);
40925                     }
40926                 }
40927             }));
40928              
40929         }
40930         tb.el.on('click', function(e){
40931             e.preventDefault(); // what does this do?
40932         });
40933         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40934         tb.el.hide();
40935         tb.name = nm;
40936         // dont need to disable them... as they will get hidden
40937         return tb;
40938          
40939         
40940     },
40941     buildFooter : function()
40942     {
40943         
40944         var fel = this.editor.wrap.createChild();
40945         this.footer = new Roo.Toolbar(fel);
40946         // toolbar has scrolly on left / right?
40947         var footDisp= new Roo.Toolbar.Fill();
40948         var _t = this;
40949         this.footer.add(
40950             {
40951                 text : '&lt;',
40952                 xtype: 'Button',
40953                 handler : function() {
40954                     _t.footDisp.scrollTo('left',0,true)
40955                 }
40956             }
40957         );
40958         this.footer.add( footDisp );
40959         this.footer.add( 
40960             {
40961                 text : '&gt;',
40962                 xtype: 'Button',
40963                 handler : function() {
40964                     // no animation..
40965                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40966                 }
40967             }
40968         );
40969         var fel = Roo.get(footDisp.el);
40970         fel.addClass('x-editor-context');
40971         this.footDispWrap = fel; 
40972         this.footDispWrap.overflow  = 'hidden';
40973         
40974         this.footDisp = fel.createChild();
40975         this.footDispWrap.on('click', this.onContextClick, this)
40976         
40977         
40978     },
40979     onContextClick : function (ev,dom)
40980     {
40981         ev.preventDefault();
40982         var  cn = dom.className;
40983         Roo.log(cn);
40984         if (!cn.match(/x-ed-loc-/)) {
40985             return;
40986         }
40987         var n = cn.split('-').pop();
40988         var ans = this.footerEls;
40989         var sel = ans[n];
40990         
40991          // pick
40992         var range = this.editor.createRange();
40993         
40994         range.selectNodeContents(sel);
40995         //range.selectNode(sel);
40996         
40997         
40998         var selection = this.editor.getSelection();
40999         selection.removeAllRanges();
41000         selection.addRange(range);
41001         
41002         
41003         
41004         this.updateToolbar(null, null, sel);
41005         
41006         
41007     }
41008     
41009     
41010     
41011     
41012     
41013 });
41014
41015
41016
41017
41018
41019 /*
41020  * Based on:
41021  * Ext JS Library 1.1.1
41022  * Copyright(c) 2006-2007, Ext JS, LLC.
41023  *
41024  * Originally Released Under LGPL - original licence link has changed is not relivant.
41025  *
41026  * Fork - LGPL
41027  * <script type="text/javascript">
41028  */
41029  
41030 /**
41031  * @class Roo.form.BasicForm
41032  * @extends Roo.util.Observable
41033  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41034  * @constructor
41035  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41036  * @param {Object} config Configuration options
41037  */
41038 Roo.form.BasicForm = function(el, config){
41039     this.allItems = [];
41040     this.childForms = [];
41041     Roo.apply(this, config);
41042     /*
41043      * The Roo.form.Field items in this form.
41044      * @type MixedCollection
41045      */
41046      
41047      
41048     this.items = new Roo.util.MixedCollection(false, function(o){
41049         return o.id || (o.id = Roo.id());
41050     });
41051     this.addEvents({
41052         /**
41053          * @event beforeaction
41054          * Fires before any action is performed. Return false to cancel the action.
41055          * @param {Form} this
41056          * @param {Action} action The action to be performed
41057          */
41058         beforeaction: true,
41059         /**
41060          * @event actionfailed
41061          * Fires when an action fails.
41062          * @param {Form} this
41063          * @param {Action} action The action that failed
41064          */
41065         actionfailed : true,
41066         /**
41067          * @event actioncomplete
41068          * Fires when an action is completed.
41069          * @param {Form} this
41070          * @param {Action} action The action that completed
41071          */
41072         actioncomplete : true
41073     });
41074     if(el){
41075         this.initEl(el);
41076     }
41077     Roo.form.BasicForm.superclass.constructor.call(this);
41078 };
41079
41080 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41081     /**
41082      * @cfg {String} method
41083      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41084      */
41085     /**
41086      * @cfg {DataReader} reader
41087      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41088      * This is optional as there is built-in support for processing JSON.
41089      */
41090     /**
41091      * @cfg {DataReader} errorReader
41092      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41093      * This is completely optional as there is built-in support for processing JSON.
41094      */
41095     /**
41096      * @cfg {String} url
41097      * The URL to use for form actions if one isn't supplied in the action options.
41098      */
41099     /**
41100      * @cfg {Boolean} fileUpload
41101      * Set to true if this form is a file upload.
41102      */
41103      
41104     /**
41105      * @cfg {Object} baseParams
41106      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41107      */
41108      /**
41109      
41110     /**
41111      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41112      */
41113     timeout: 30,
41114
41115     // private
41116     activeAction : null,
41117
41118     /**
41119      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41120      * or setValues() data instead of when the form was first created.
41121      */
41122     trackResetOnLoad : false,
41123     
41124     
41125     /**
41126      * childForms - used for multi-tab forms
41127      * @type {Array}
41128      */
41129     childForms : false,
41130     
41131     /**
41132      * allItems - full list of fields.
41133      * @type {Array}
41134      */
41135     allItems : false,
41136     
41137     /**
41138      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41139      * element by passing it or its id or mask the form itself by passing in true.
41140      * @type Mixed
41141      */
41142     waitMsgTarget : false,
41143
41144     // private
41145     initEl : function(el){
41146         this.el = Roo.get(el);
41147         this.id = this.el.id || Roo.id();
41148         this.el.on('submit', this.onSubmit, this);
41149         this.el.addClass('x-form');
41150     },
41151
41152     // private
41153     onSubmit : function(e){
41154         e.stopEvent();
41155     },
41156
41157     /**
41158      * Returns true if client-side validation on the form is successful.
41159      * @return Boolean
41160      */
41161     isValid : function(){
41162         var valid = true;
41163         this.items.each(function(f){
41164            if(!f.validate()){
41165                valid = false;
41166            }
41167         });
41168         return valid;
41169     },
41170
41171     /**
41172      * Returns true if any fields in this form have changed since their original load.
41173      * @return Boolean
41174      */
41175     isDirty : function(){
41176         var dirty = false;
41177         this.items.each(function(f){
41178            if(f.isDirty()){
41179                dirty = true;
41180                return false;
41181            }
41182         });
41183         return dirty;
41184     },
41185
41186     /**
41187      * Performs a predefined action (submit or load) or custom actions you define on this form.
41188      * @param {String} actionName The name of the action type
41189      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41190      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41191      * accept other config options):
41192      * <pre>
41193 Property          Type             Description
41194 ----------------  ---------------  ----------------------------------------------------------------------------------
41195 url               String           The url for the action (defaults to the form's url)
41196 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41197 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41198 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41199                                    validate the form on the client (defaults to false)
41200      * </pre>
41201      * @return {BasicForm} this
41202      */
41203     doAction : function(action, options){
41204         if(typeof action == 'string'){
41205             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41206         }
41207         if(this.fireEvent('beforeaction', this, action) !== false){
41208             this.beforeAction(action);
41209             action.run.defer(100, action);
41210         }
41211         return this;
41212     },
41213
41214     /**
41215      * Shortcut to do a submit action.
41216      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41217      * @return {BasicForm} this
41218      */
41219     submit : function(options){
41220         this.doAction('submit', options);
41221         return this;
41222     },
41223
41224     /**
41225      * Shortcut to do a load action.
41226      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41227      * @return {BasicForm} this
41228      */
41229     load : function(options){
41230         this.doAction('load', options);
41231         return this;
41232     },
41233
41234     /**
41235      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41236      * @param {Record} record The record to edit
41237      * @return {BasicForm} this
41238      */
41239     updateRecord : function(record){
41240         record.beginEdit();
41241         var fs = record.fields;
41242         fs.each(function(f){
41243             var field = this.findField(f.name);
41244             if(field){
41245                 record.set(f.name, field.getValue());
41246             }
41247         }, this);
41248         record.endEdit();
41249         return this;
41250     },
41251
41252     /**
41253      * Loads an Roo.data.Record into this form.
41254      * @param {Record} record The record to load
41255      * @return {BasicForm} this
41256      */
41257     loadRecord : function(record){
41258         this.setValues(record.data);
41259         return this;
41260     },
41261
41262     // private
41263     beforeAction : function(action){
41264         var o = action.options;
41265         
41266        
41267         if(this.waitMsgTarget === true){
41268             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41269         }else if(this.waitMsgTarget){
41270             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41271             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41272         }else {
41273             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41274         }
41275          
41276     },
41277
41278     // private
41279     afterAction : function(action, success){
41280         this.activeAction = null;
41281         var o = action.options;
41282         
41283         if(this.waitMsgTarget === true){
41284             this.el.unmask();
41285         }else if(this.waitMsgTarget){
41286             this.waitMsgTarget.unmask();
41287         }else{
41288             Roo.MessageBox.updateProgress(1);
41289             Roo.MessageBox.hide();
41290         }
41291          
41292         if(success){
41293             if(o.reset){
41294                 this.reset();
41295             }
41296             Roo.callback(o.success, o.scope, [this, action]);
41297             this.fireEvent('actioncomplete', this, action);
41298             
41299         }else{
41300             Roo.callback(o.failure, o.scope, [this, action]);
41301             // show an error message if no failed handler is set..
41302             if (!this.hasListener('actionfailed')) {
41303                 Roo.MessageBox.alert("Error",
41304                     typeof(action.result.errorMsg) != 'undefined' ?
41305                         action.result.errorMsg :
41306                         "Saving Failed, please check your entries"
41307                 );
41308             }
41309             
41310             this.fireEvent('actionfailed', this, action);
41311         }
41312         
41313     },
41314
41315     /**
41316      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41317      * @param {String} id The value to search for
41318      * @return Field
41319      */
41320     findField : function(id){
41321         var field = this.items.get(id);
41322         if(!field){
41323             this.items.each(function(f){
41324                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41325                     field = f;
41326                     return false;
41327                 }
41328             });
41329         }
41330         return field || null;
41331     },
41332
41333     /**
41334      * Add a secondary form to this one, 
41335      * Used to provide tabbed forms. One form is primary, with hidden values 
41336      * which mirror the elements from the other forms.
41337      * 
41338      * @param {Roo.form.Form} form to add.
41339      * 
41340      */
41341     addForm : function(form)
41342     {
41343        
41344         if (this.childForms.indexOf(form) > -1) {
41345             // already added..
41346             return;
41347         }
41348         this.childForms.push(form);
41349         var n = '';
41350         Roo.each(form.allItems, function (fe) {
41351             
41352             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41353             if (this.findField(n)) { // already added..
41354                 return;
41355             }
41356             var add = new Roo.form.Hidden({
41357                 name : n
41358             });
41359             add.render(this.el);
41360             
41361             this.add( add );
41362         }, this);
41363         
41364     },
41365     /**
41366      * Mark fields in this form invalid in bulk.
41367      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41368      * @return {BasicForm} this
41369      */
41370     markInvalid : function(errors){
41371         if(errors instanceof Array){
41372             for(var i = 0, len = errors.length; i < len; i++){
41373                 var fieldError = errors[i];
41374                 var f = this.findField(fieldError.id);
41375                 if(f){
41376                     f.markInvalid(fieldError.msg);
41377                 }
41378             }
41379         }else{
41380             var field, id;
41381             for(id in errors){
41382                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41383                     field.markInvalid(errors[id]);
41384                 }
41385             }
41386         }
41387         Roo.each(this.childForms || [], function (f) {
41388             f.markInvalid(errors);
41389         });
41390         
41391         return this;
41392     },
41393
41394     /**
41395      * Set values for fields in this form in bulk.
41396      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41397      * @return {BasicForm} this
41398      */
41399     setValues : function(values){
41400         if(values instanceof Array){ // array of objects
41401             for(var i = 0, len = values.length; i < len; i++){
41402                 var v = values[i];
41403                 var f = this.findField(v.id);
41404                 if(f){
41405                     f.setValue(v.value);
41406                     if(this.trackResetOnLoad){
41407                         f.originalValue = f.getValue();
41408                     }
41409                 }
41410             }
41411         }else{ // object hash
41412             var field, id;
41413             for(id in values){
41414                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41415                     
41416                     if (field.setFromData && 
41417                         field.valueField && 
41418                         field.displayField &&
41419                         // combos' with local stores can 
41420                         // be queried via setValue()
41421                         // to set their value..
41422                         (field.store && !field.store.isLocal)
41423                         ) {
41424                         // it's a combo
41425                         var sd = { };
41426                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41427                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41428                         field.setFromData(sd);
41429                         
41430                     } else {
41431                         field.setValue(values[id]);
41432                     }
41433                     
41434                     
41435                     if(this.trackResetOnLoad){
41436                         field.originalValue = field.getValue();
41437                     }
41438                 }
41439             }
41440         }
41441          
41442         Roo.each(this.childForms || [], function (f) {
41443             f.setValues(values);
41444         });
41445                 
41446         return this;
41447     },
41448
41449     /**
41450      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41451      * they are returned as an array.
41452      * @param {Boolean} asString
41453      * @return {Object}
41454      */
41455     getValues : function(asString){
41456         if (this.childForms) {
41457             // copy values from the child forms
41458             Roo.each(this.childForms, function (f) {
41459                 this.setValues(f.getValues());
41460             }, this);
41461         }
41462         
41463         
41464         
41465         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41466         if(asString === true){
41467             return fs;
41468         }
41469         return Roo.urlDecode(fs);
41470     },
41471     
41472     /**
41473      * Returns the fields in this form as an object with key/value pairs. 
41474      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41475      * @return {Object}
41476      */
41477     getFieldValues : function(with_hidden)
41478     {
41479         if (this.childForms) {
41480             // copy values from the child forms
41481             // should this call getFieldValues - probably not as we do not currently copy
41482             // hidden fields when we generate..
41483             Roo.each(this.childForms, function (f) {
41484                 this.setValues(f.getValues());
41485             }, this);
41486         }
41487         
41488         var ret = {};
41489         this.items.each(function(f){
41490             if (!f.getName()) {
41491                 return;
41492             }
41493             var v = f.getValue();
41494             // not sure if this supported any more..
41495             if ((typeof(v) == 'object') && f.getRawValue) {
41496                 v = f.getRawValue() ; // dates..
41497             }
41498             // combo boxes where name != hiddenName...
41499             if (f.name != f.getName()) {
41500                 ret[f.name] = f.getRawValue();
41501             }
41502             ret[f.getName()] = v;
41503         });
41504         
41505         return ret;
41506     },
41507
41508     /**
41509      * Clears all invalid messages in this form.
41510      * @return {BasicForm} this
41511      */
41512     clearInvalid : function(){
41513         this.items.each(function(f){
41514            f.clearInvalid();
41515         });
41516         
41517         Roo.each(this.childForms || [], function (f) {
41518             f.clearInvalid();
41519         });
41520         
41521         
41522         return this;
41523     },
41524
41525     /**
41526      * Resets this form.
41527      * @return {BasicForm} this
41528      */
41529     reset : function(){
41530         this.items.each(function(f){
41531             f.reset();
41532         });
41533         
41534         Roo.each(this.childForms || [], function (f) {
41535             f.reset();
41536         });
41537        
41538         
41539         return this;
41540     },
41541
41542     /**
41543      * Add Roo.form components to this form.
41544      * @param {Field} field1
41545      * @param {Field} field2 (optional)
41546      * @param {Field} etc (optional)
41547      * @return {BasicForm} this
41548      */
41549     add : function(){
41550         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41551         return this;
41552     },
41553
41554
41555     /**
41556      * Removes a field from the items collection (does NOT remove its markup).
41557      * @param {Field} field
41558      * @return {BasicForm} this
41559      */
41560     remove : function(field){
41561         this.items.remove(field);
41562         return this;
41563     },
41564
41565     /**
41566      * Looks at the fields in this form, checks them for an id attribute,
41567      * and calls applyTo on the existing dom element with that id.
41568      * @return {BasicForm} this
41569      */
41570     render : function(){
41571         this.items.each(function(f){
41572             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41573                 f.applyTo(f.id);
41574             }
41575         });
41576         return this;
41577     },
41578
41579     /**
41580      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41581      * @param {Object} values
41582      * @return {BasicForm} this
41583      */
41584     applyToFields : function(o){
41585         this.items.each(function(f){
41586            Roo.apply(f, o);
41587         });
41588         return this;
41589     },
41590
41591     /**
41592      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41593      * @param {Object} values
41594      * @return {BasicForm} this
41595      */
41596     applyIfToFields : function(o){
41597         this.items.each(function(f){
41598            Roo.applyIf(f, o);
41599         });
41600         return this;
41601     }
41602 });
41603
41604 // back compat
41605 Roo.BasicForm = Roo.form.BasicForm;/*
41606  * Based on:
41607  * Ext JS Library 1.1.1
41608  * Copyright(c) 2006-2007, Ext JS, LLC.
41609  *
41610  * Originally Released Under LGPL - original licence link has changed is not relivant.
41611  *
41612  * Fork - LGPL
41613  * <script type="text/javascript">
41614  */
41615
41616 /**
41617  * @class Roo.form.Form
41618  * @extends Roo.form.BasicForm
41619  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41620  * @constructor
41621  * @param {Object} config Configuration options
41622  */
41623 Roo.form.Form = function(config){
41624     var xitems =  [];
41625     if (config.items) {
41626         xitems = config.items;
41627         delete config.items;
41628     }
41629    
41630     
41631     Roo.form.Form.superclass.constructor.call(this, null, config);
41632     this.url = this.url || this.action;
41633     if(!this.root){
41634         this.root = new Roo.form.Layout(Roo.applyIf({
41635             id: Roo.id()
41636         }, config));
41637     }
41638     this.active = this.root;
41639     /**
41640      * Array of all the buttons that have been added to this form via {@link addButton}
41641      * @type Array
41642      */
41643     this.buttons = [];
41644     this.allItems = [];
41645     this.addEvents({
41646         /**
41647          * @event clientvalidation
41648          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41649          * @param {Form} this
41650          * @param {Boolean} valid true if the form has passed client-side validation
41651          */
41652         clientvalidation: true,
41653         /**
41654          * @event rendered
41655          * Fires when the form is rendered
41656          * @param {Roo.form.Form} form
41657          */
41658         rendered : true
41659     });
41660     
41661     if (this.progressUrl) {
41662             // push a hidden field onto the list of fields..
41663             this.addxtype( {
41664                     xns: Roo.form, 
41665                     xtype : 'Hidden', 
41666                     name : 'UPLOAD_IDENTIFIER' 
41667             });
41668         }
41669         
41670     
41671     Roo.each(xitems, this.addxtype, this);
41672     
41673     
41674     
41675 };
41676
41677 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41678     /**
41679      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41680      */
41681     /**
41682      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41683      */
41684     /**
41685      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41686      */
41687     buttonAlign:'center',
41688
41689     /**
41690      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41691      */
41692     minButtonWidth:75,
41693
41694     /**
41695      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41696      * This property cascades to child containers if not set.
41697      */
41698     labelAlign:'left',
41699
41700     /**
41701      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41702      * fires a looping event with that state. This is required to bind buttons to the valid
41703      * state using the config value formBind:true on the button.
41704      */
41705     monitorValid : false,
41706
41707     /**
41708      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41709      */
41710     monitorPoll : 200,
41711     
41712     /**
41713      * @cfg {String} progressUrl - Url to return progress data 
41714      */
41715     
41716     progressUrl : false,
41717   
41718     /**
41719      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41720      * fields are added and the column is closed. If no fields are passed the column remains open
41721      * until end() is called.
41722      * @param {Object} config The config to pass to the column
41723      * @param {Field} field1 (optional)
41724      * @param {Field} field2 (optional)
41725      * @param {Field} etc (optional)
41726      * @return Column The column container object
41727      */
41728     column : function(c){
41729         var col = new Roo.form.Column(c);
41730         this.start(col);
41731         if(arguments.length > 1){ // duplicate code required because of Opera
41732             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41733             this.end();
41734         }
41735         return col;
41736     },
41737
41738     /**
41739      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41740      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41741      * until end() is called.
41742      * @param {Object} config The config to pass to the fieldset
41743      * @param {Field} field1 (optional)
41744      * @param {Field} field2 (optional)
41745      * @param {Field} etc (optional)
41746      * @return FieldSet The fieldset container object
41747      */
41748     fieldset : function(c){
41749         var fs = new Roo.form.FieldSet(c);
41750         this.start(fs);
41751         if(arguments.length > 1){ // duplicate code required because of Opera
41752             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41753             this.end();
41754         }
41755         return fs;
41756     },
41757
41758     /**
41759      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41760      * fields are added and the container is closed. If no fields are passed the container remains open
41761      * until end() is called.
41762      * @param {Object} config The config to pass to the Layout
41763      * @param {Field} field1 (optional)
41764      * @param {Field} field2 (optional)
41765      * @param {Field} etc (optional)
41766      * @return Layout The container object
41767      */
41768     container : function(c){
41769         var l = new Roo.form.Layout(c);
41770         this.start(l);
41771         if(arguments.length > 1){ // duplicate code required because of Opera
41772             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41773             this.end();
41774         }
41775         return l;
41776     },
41777
41778     /**
41779      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41780      * @param {Object} container A Roo.form.Layout or subclass of Layout
41781      * @return {Form} this
41782      */
41783     start : function(c){
41784         // cascade label info
41785         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41786         this.active.stack.push(c);
41787         c.ownerCt = this.active;
41788         this.active = c;
41789         return this;
41790     },
41791
41792     /**
41793      * Closes the current open container
41794      * @return {Form} this
41795      */
41796     end : function(){
41797         if(this.active == this.root){
41798             return this;
41799         }
41800         this.active = this.active.ownerCt;
41801         return this;
41802     },
41803
41804     /**
41805      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41806      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41807      * as the label of the field.
41808      * @param {Field} field1
41809      * @param {Field} field2 (optional)
41810      * @param {Field} etc. (optional)
41811      * @return {Form} this
41812      */
41813     add : function(){
41814         this.active.stack.push.apply(this.active.stack, arguments);
41815         this.allItems.push.apply(this.allItems,arguments);
41816         var r = [];
41817         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41818             if(a[i].isFormField){
41819                 r.push(a[i]);
41820             }
41821         }
41822         if(r.length > 0){
41823             Roo.form.Form.superclass.add.apply(this, r);
41824         }
41825         return this;
41826     },
41827     
41828
41829     
41830     
41831     
41832      /**
41833      * Find any element that has been added to a form, using it's ID or name
41834      * This can include framesets, columns etc. along with regular fields..
41835      * @param {String} id - id or name to find.
41836      
41837      * @return {Element} e - or false if nothing found.
41838      */
41839     findbyId : function(id)
41840     {
41841         var ret = false;
41842         if (!id) {
41843             return ret;
41844         }
41845         Roo.each(this.allItems, function(f){
41846             if (f.id == id || f.name == id ){
41847                 ret = f;
41848                 return false;
41849             }
41850         });
41851         return ret;
41852     },
41853
41854     
41855     
41856     /**
41857      * Render this form into the passed container. This should only be called once!
41858      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41859      * @return {Form} this
41860      */
41861     render : function(ct)
41862     {
41863         
41864         
41865         
41866         ct = Roo.get(ct);
41867         var o = this.autoCreate || {
41868             tag: 'form',
41869             method : this.method || 'POST',
41870             id : this.id || Roo.id()
41871         };
41872         this.initEl(ct.createChild(o));
41873
41874         this.root.render(this.el);
41875         
41876        
41877              
41878         this.items.each(function(f){
41879             f.render('x-form-el-'+f.id);
41880         });
41881
41882         if(this.buttons.length > 0){
41883             // tables are required to maintain order and for correct IE layout
41884             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41885                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41886                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41887             }}, null, true);
41888             var tr = tb.getElementsByTagName('tr')[0];
41889             for(var i = 0, len = this.buttons.length; i < len; i++) {
41890                 var b = this.buttons[i];
41891                 var td = document.createElement('td');
41892                 td.className = 'x-form-btn-td';
41893                 b.render(tr.appendChild(td));
41894             }
41895         }
41896         if(this.monitorValid){ // initialize after render
41897             this.startMonitoring();
41898         }
41899         this.fireEvent('rendered', this);
41900         return this;
41901     },
41902
41903     /**
41904      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41905      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41906      * object or a valid Roo.DomHelper element config
41907      * @param {Function} handler The function called when the button is clicked
41908      * @param {Object} scope (optional) The scope of the handler function
41909      * @return {Roo.Button}
41910      */
41911     addButton : function(config, handler, scope){
41912         var bc = {
41913             handler: handler,
41914             scope: scope,
41915             minWidth: this.minButtonWidth,
41916             hideParent:true
41917         };
41918         if(typeof config == "string"){
41919             bc.text = config;
41920         }else{
41921             Roo.apply(bc, config);
41922         }
41923         var btn = new Roo.Button(null, bc);
41924         this.buttons.push(btn);
41925         return btn;
41926     },
41927
41928      /**
41929      * Adds a series of form elements (using the xtype property as the factory method.
41930      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41931      * @param {Object} config 
41932      */
41933     
41934     addxtype : function()
41935     {
41936         var ar = Array.prototype.slice.call(arguments, 0);
41937         var ret = false;
41938         for(var i = 0; i < ar.length; i++) {
41939             if (!ar[i]) {
41940                 continue; // skip -- if this happends something invalid got sent, we 
41941                 // should ignore it, as basically that interface element will not show up
41942                 // and that should be pretty obvious!!
41943             }
41944             
41945             if (Roo.form[ar[i].xtype]) {
41946                 ar[i].form = this;
41947                 var fe = Roo.factory(ar[i], Roo.form);
41948                 if (!ret) {
41949                     ret = fe;
41950                 }
41951                 fe.form = this;
41952                 if (fe.store) {
41953                     fe.store.form = this;
41954                 }
41955                 if (fe.isLayout) {  
41956                          
41957                     this.start(fe);
41958                     this.allItems.push(fe);
41959                     if (fe.items && fe.addxtype) {
41960                         fe.addxtype.apply(fe, fe.items);
41961                         delete fe.items;
41962                     }
41963                      this.end();
41964                     continue;
41965                 }
41966                 
41967                 
41968                  
41969                 this.add(fe);
41970               //  console.log('adding ' + ar[i].xtype);
41971             }
41972             if (ar[i].xtype == 'Button') {  
41973                 //console.log('adding button');
41974                 //console.log(ar[i]);
41975                 this.addButton(ar[i]);
41976                 this.allItems.push(fe);
41977                 continue;
41978             }
41979             
41980             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41981                 alert('end is not supported on xtype any more, use items');
41982             //    this.end();
41983             //    //console.log('adding end');
41984             }
41985             
41986         }
41987         return ret;
41988     },
41989     
41990     /**
41991      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41992      * option "monitorValid"
41993      */
41994     startMonitoring : function(){
41995         if(!this.bound){
41996             this.bound = true;
41997             Roo.TaskMgr.start({
41998                 run : this.bindHandler,
41999                 interval : this.monitorPoll || 200,
42000                 scope: this
42001             });
42002         }
42003     },
42004
42005     /**
42006      * Stops monitoring of the valid state of this form
42007      */
42008     stopMonitoring : function(){
42009         this.bound = false;
42010     },
42011
42012     // private
42013     bindHandler : function(){
42014         if(!this.bound){
42015             return false; // stops binding
42016         }
42017         var valid = true;
42018         this.items.each(function(f){
42019             if(!f.isValid(true)){
42020                 valid = false;
42021                 return false;
42022             }
42023         });
42024         for(var i = 0, len = this.buttons.length; i < len; i++){
42025             var btn = this.buttons[i];
42026             if(btn.formBind === true && btn.disabled === valid){
42027                 btn.setDisabled(!valid);
42028             }
42029         }
42030         this.fireEvent('clientvalidation', this, valid);
42031     }
42032     
42033     
42034     
42035     
42036     
42037     
42038     
42039     
42040 });
42041
42042
42043 // back compat
42044 Roo.Form = Roo.form.Form;
42045 /*
42046  * Based on:
42047  * Ext JS Library 1.1.1
42048  * Copyright(c) 2006-2007, Ext JS, LLC.
42049  *
42050  * Originally Released Under LGPL - original licence link has changed is not relivant.
42051  *
42052  * Fork - LGPL
42053  * <script type="text/javascript">
42054  */
42055  
42056  /**
42057  * @class Roo.form.Action
42058  * Internal Class used to handle form actions
42059  * @constructor
42060  * @param {Roo.form.BasicForm} el The form element or its id
42061  * @param {Object} config Configuration options
42062  */
42063  
42064  
42065 // define the action interface
42066 Roo.form.Action = function(form, options){
42067     this.form = form;
42068     this.options = options || {};
42069 };
42070 /**
42071  * Client Validation Failed
42072  * @const 
42073  */
42074 Roo.form.Action.CLIENT_INVALID = 'client';
42075 /**
42076  * Server Validation Failed
42077  * @const 
42078  */
42079  Roo.form.Action.SERVER_INVALID = 'server';
42080  /**
42081  * Connect to Server Failed
42082  * @const 
42083  */
42084 Roo.form.Action.CONNECT_FAILURE = 'connect';
42085 /**
42086  * Reading Data from Server Failed
42087  * @const 
42088  */
42089 Roo.form.Action.LOAD_FAILURE = 'load';
42090
42091 Roo.form.Action.prototype = {
42092     type : 'default',
42093     failureType : undefined,
42094     response : undefined,
42095     result : undefined,
42096
42097     // interface method
42098     run : function(options){
42099
42100     },
42101
42102     // interface method
42103     success : function(response){
42104
42105     },
42106
42107     // interface method
42108     handleResponse : function(response){
42109
42110     },
42111
42112     // default connection failure
42113     failure : function(response){
42114         
42115         this.response = response;
42116         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42117         this.form.afterAction(this, false);
42118     },
42119
42120     processResponse : function(response){
42121         this.response = response;
42122         if(!response.responseText){
42123             return true;
42124         }
42125         this.result = this.handleResponse(response);
42126         return this.result;
42127     },
42128
42129     // utility functions used internally
42130     getUrl : function(appendParams){
42131         var url = this.options.url || this.form.url || this.form.el.dom.action;
42132         if(appendParams){
42133             var p = this.getParams();
42134             if(p){
42135                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42136             }
42137         }
42138         return url;
42139     },
42140
42141     getMethod : function(){
42142         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42143     },
42144
42145     getParams : function(){
42146         var bp = this.form.baseParams;
42147         var p = this.options.params;
42148         if(p){
42149             if(typeof p == "object"){
42150                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42151             }else if(typeof p == 'string' && bp){
42152                 p += '&' + Roo.urlEncode(bp);
42153             }
42154         }else if(bp){
42155             p = Roo.urlEncode(bp);
42156         }
42157         return p;
42158     },
42159
42160     createCallback : function(){
42161         return {
42162             success: this.success,
42163             failure: this.failure,
42164             scope: this,
42165             timeout: (this.form.timeout*1000),
42166             upload: this.form.fileUpload ? this.success : undefined
42167         };
42168     }
42169 };
42170
42171 Roo.form.Action.Submit = function(form, options){
42172     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42173 };
42174
42175 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42176     type : 'submit',
42177
42178     haveProgress : false,
42179     uploadComplete : false,
42180     
42181     // uploadProgress indicator.
42182     uploadProgress : function()
42183     {
42184         if (!this.form.progressUrl) {
42185             return;
42186         }
42187         
42188         if (!this.haveProgress) {
42189             Roo.MessageBox.progress("Uploading", "Uploading");
42190         }
42191         if (this.uploadComplete) {
42192            Roo.MessageBox.hide();
42193            return;
42194         }
42195         
42196         this.haveProgress = true;
42197    
42198         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42199         
42200         var c = new Roo.data.Connection();
42201         c.request({
42202             url : this.form.progressUrl,
42203             params: {
42204                 id : uid
42205             },
42206             method: 'GET',
42207             success : function(req){
42208                //console.log(data);
42209                 var rdata = false;
42210                 var edata;
42211                 try  {
42212                    rdata = Roo.decode(req.responseText)
42213                 } catch (e) {
42214                     Roo.log("Invalid data from server..");
42215                     Roo.log(edata);
42216                     return;
42217                 }
42218                 if (!rdata || !rdata.success) {
42219                     Roo.log(rdata);
42220                     return;
42221                 }
42222                 var data = rdata.data;
42223                 
42224                 if (this.uploadComplete) {
42225                    Roo.MessageBox.hide();
42226                    return;
42227                 }
42228                    
42229                 if (data){
42230                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42231                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42232                     );
42233                 }
42234                 this.uploadProgress.defer(2000,this);
42235             },
42236        
42237             failure: function(data) {
42238                 Roo.log('progress url failed ');
42239                 Roo.log(data);
42240             },
42241             scope : this
42242         });
42243            
42244     },
42245     
42246     
42247     run : function()
42248     {
42249         // run get Values on the form, so it syncs any secondary forms.
42250         this.form.getValues();
42251         
42252         var o = this.options;
42253         var method = this.getMethod();
42254         var isPost = method == 'POST';
42255         if(o.clientValidation === false || this.form.isValid()){
42256             
42257             if (this.form.progressUrl) {
42258                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42259                     (new Date() * 1) + '' + Math.random());
42260                     
42261             } 
42262             
42263             
42264             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42265                 form:this.form.el.dom,
42266                 url:this.getUrl(!isPost),
42267                 method: method,
42268                 params:isPost ? this.getParams() : null,
42269                 isUpload: this.form.fileUpload
42270             }));
42271             
42272             this.uploadProgress();
42273
42274         }else if (o.clientValidation !== false){ // client validation failed
42275             this.failureType = Roo.form.Action.CLIENT_INVALID;
42276             this.form.afterAction(this, false);
42277         }
42278     },
42279
42280     success : function(response)
42281     {
42282         this.uploadComplete= true;
42283         if (this.haveProgress) {
42284             Roo.MessageBox.hide();
42285         }
42286         
42287         
42288         var result = this.processResponse(response);
42289         if(result === true || result.success){
42290             this.form.afterAction(this, true);
42291             return;
42292         }
42293         if(result.errors){
42294             this.form.markInvalid(result.errors);
42295             this.failureType = Roo.form.Action.SERVER_INVALID;
42296         }
42297         this.form.afterAction(this, false);
42298     },
42299     failure : function(response)
42300     {
42301         this.uploadComplete= true;
42302         if (this.haveProgress) {
42303             Roo.MessageBox.hide();
42304         }
42305         
42306         this.response = response;
42307         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42308         this.form.afterAction(this, false);
42309     },
42310     
42311     handleResponse : function(response){
42312         if(this.form.errorReader){
42313             var rs = this.form.errorReader.read(response);
42314             var errors = [];
42315             if(rs.records){
42316                 for(var i = 0, len = rs.records.length; i < len; i++) {
42317                     var r = rs.records[i];
42318                     errors[i] = r.data;
42319                 }
42320             }
42321             if(errors.length < 1){
42322                 errors = null;
42323             }
42324             return {
42325                 success : rs.success,
42326                 errors : errors
42327             };
42328         }
42329         var ret = false;
42330         try {
42331             ret = Roo.decode(response.responseText);
42332         } catch (e) {
42333             ret = {
42334                 success: false,
42335                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42336                 errors : []
42337             };
42338         }
42339         return ret;
42340         
42341     }
42342 });
42343
42344
42345 Roo.form.Action.Load = function(form, options){
42346     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42347     this.reader = this.form.reader;
42348 };
42349
42350 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42351     type : 'load',
42352
42353     run : function(){
42354         
42355         Roo.Ajax.request(Roo.apply(
42356                 this.createCallback(), {
42357                     method:this.getMethod(),
42358                     url:this.getUrl(false),
42359                     params:this.getParams()
42360         }));
42361     },
42362
42363     success : function(response){
42364         
42365         var result = this.processResponse(response);
42366         if(result === true || !result.success || !result.data){
42367             this.failureType = Roo.form.Action.LOAD_FAILURE;
42368             this.form.afterAction(this, false);
42369             return;
42370         }
42371         this.form.clearInvalid();
42372         this.form.setValues(result.data);
42373         this.form.afterAction(this, true);
42374     },
42375
42376     handleResponse : function(response){
42377         if(this.form.reader){
42378             var rs = this.form.reader.read(response);
42379             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42380             return {
42381                 success : rs.success,
42382                 data : data
42383             };
42384         }
42385         return Roo.decode(response.responseText);
42386     }
42387 });
42388
42389 Roo.form.Action.ACTION_TYPES = {
42390     'load' : Roo.form.Action.Load,
42391     'submit' : Roo.form.Action.Submit
42392 };/*
42393  * Based on:
42394  * Ext JS Library 1.1.1
42395  * Copyright(c) 2006-2007, Ext JS, LLC.
42396  *
42397  * Originally Released Under LGPL - original licence link has changed is not relivant.
42398  *
42399  * Fork - LGPL
42400  * <script type="text/javascript">
42401  */
42402  
42403 /**
42404  * @class Roo.form.Layout
42405  * @extends Roo.Component
42406  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42407  * @constructor
42408  * @param {Object} config Configuration options
42409  */
42410 Roo.form.Layout = function(config){
42411     var xitems = [];
42412     if (config.items) {
42413         xitems = config.items;
42414         delete config.items;
42415     }
42416     Roo.form.Layout.superclass.constructor.call(this, config);
42417     this.stack = [];
42418     Roo.each(xitems, this.addxtype, this);
42419      
42420 };
42421
42422 Roo.extend(Roo.form.Layout, Roo.Component, {
42423     /**
42424      * @cfg {String/Object} autoCreate
42425      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42426      */
42427     /**
42428      * @cfg {String/Object/Function} style
42429      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42430      * a function which returns such a specification.
42431      */
42432     /**
42433      * @cfg {String} labelAlign
42434      * Valid values are "left," "top" and "right" (defaults to "left")
42435      */
42436     /**
42437      * @cfg {Number} labelWidth
42438      * Fixed width in pixels of all field labels (defaults to undefined)
42439      */
42440     /**
42441      * @cfg {Boolean} clear
42442      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42443      */
42444     clear : true,
42445     /**
42446      * @cfg {String} labelSeparator
42447      * The separator to use after field labels (defaults to ':')
42448      */
42449     labelSeparator : ':',
42450     /**
42451      * @cfg {Boolean} hideLabels
42452      * True to suppress the display of field labels in this layout (defaults to false)
42453      */
42454     hideLabels : false,
42455
42456     // private
42457     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42458     
42459     isLayout : true,
42460     
42461     // private
42462     onRender : function(ct, position){
42463         if(this.el){ // from markup
42464             this.el = Roo.get(this.el);
42465         }else {  // generate
42466             var cfg = this.getAutoCreate();
42467             this.el = ct.createChild(cfg, position);
42468         }
42469         if(this.style){
42470             this.el.applyStyles(this.style);
42471         }
42472         if(this.labelAlign){
42473             this.el.addClass('x-form-label-'+this.labelAlign);
42474         }
42475         if(this.hideLabels){
42476             this.labelStyle = "display:none";
42477             this.elementStyle = "padding-left:0;";
42478         }else{
42479             if(typeof this.labelWidth == 'number'){
42480                 this.labelStyle = "width:"+this.labelWidth+"px;";
42481                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42482             }
42483             if(this.labelAlign == 'top'){
42484                 this.labelStyle = "width:auto;";
42485                 this.elementStyle = "padding-left:0;";
42486             }
42487         }
42488         var stack = this.stack;
42489         var slen = stack.length;
42490         if(slen > 0){
42491             if(!this.fieldTpl){
42492                 var t = new Roo.Template(
42493                     '<div class="x-form-item {5}">',
42494                         '<label for="{0}" style="{2}">{1}{4}</label>',
42495                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42496                         '</div>',
42497                     '</div><div class="x-form-clear-left"></div>'
42498                 );
42499                 t.disableFormats = true;
42500                 t.compile();
42501                 Roo.form.Layout.prototype.fieldTpl = t;
42502             }
42503             for(var i = 0; i < slen; i++) {
42504                 if(stack[i].isFormField){
42505                     this.renderField(stack[i]);
42506                 }else{
42507                     this.renderComponent(stack[i]);
42508                 }
42509             }
42510         }
42511         if(this.clear){
42512             this.el.createChild({cls:'x-form-clear'});
42513         }
42514     },
42515
42516     // private
42517     renderField : function(f){
42518         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42519                f.id, //0
42520                f.fieldLabel, //1
42521                f.labelStyle||this.labelStyle||'', //2
42522                this.elementStyle||'', //3
42523                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42524                f.itemCls||this.itemCls||''  //5
42525        ], true).getPrevSibling());
42526     },
42527
42528     // private
42529     renderComponent : function(c){
42530         c.render(c.isLayout ? this.el : this.el.createChild());    
42531     },
42532     /**
42533      * Adds a object form elements (using the xtype property as the factory method.)
42534      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42535      * @param {Object} config 
42536      */
42537     addxtype : function(o)
42538     {
42539         // create the lement.
42540         o.form = this.form;
42541         var fe = Roo.factory(o, Roo.form);
42542         this.form.allItems.push(fe);
42543         this.stack.push(fe);
42544         
42545         if (fe.isFormField) {
42546             this.form.items.add(fe);
42547         }
42548          
42549         return fe;
42550     }
42551 });
42552
42553 /**
42554  * @class Roo.form.Column
42555  * @extends Roo.form.Layout
42556  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42557  * @constructor
42558  * @param {Object} config Configuration options
42559  */
42560 Roo.form.Column = function(config){
42561     Roo.form.Column.superclass.constructor.call(this, config);
42562 };
42563
42564 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42565     /**
42566      * @cfg {Number/String} width
42567      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42568      */
42569     /**
42570      * @cfg {String/Object} autoCreate
42571      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42572      */
42573
42574     // private
42575     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42576
42577     // private
42578     onRender : function(ct, position){
42579         Roo.form.Column.superclass.onRender.call(this, ct, position);
42580         if(this.width){
42581             this.el.setWidth(this.width);
42582         }
42583     }
42584 });
42585
42586
42587 /**
42588  * @class Roo.form.Row
42589  * @extends Roo.form.Layout
42590  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42591  * @constructor
42592  * @param {Object} config Configuration options
42593  */
42594
42595  
42596 Roo.form.Row = function(config){
42597     Roo.form.Row.superclass.constructor.call(this, config);
42598 };
42599  
42600 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42601       /**
42602      * @cfg {Number/String} width
42603      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42604      */
42605     /**
42606      * @cfg {Number/String} height
42607      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42608      */
42609     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42610     
42611     padWidth : 20,
42612     // private
42613     onRender : function(ct, position){
42614         //console.log('row render');
42615         if(!this.rowTpl){
42616             var t = new Roo.Template(
42617                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42618                     '<label for="{0}" style="{2}">{1}{4}</label>',
42619                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42620                     '</div>',
42621                 '</div>'
42622             );
42623             t.disableFormats = true;
42624             t.compile();
42625             Roo.form.Layout.prototype.rowTpl = t;
42626         }
42627         this.fieldTpl = this.rowTpl;
42628         
42629         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42630         var labelWidth = 100;
42631         
42632         if ((this.labelAlign != 'top')) {
42633             if (typeof this.labelWidth == 'number') {
42634                 labelWidth = this.labelWidth
42635             }
42636             this.padWidth =  20 + labelWidth;
42637             
42638         }
42639         
42640         Roo.form.Column.superclass.onRender.call(this, ct, position);
42641         if(this.width){
42642             this.el.setWidth(this.width);
42643         }
42644         if(this.height){
42645             this.el.setHeight(this.height);
42646         }
42647     },
42648     
42649     // private
42650     renderField : function(f){
42651         f.fieldEl = this.fieldTpl.append(this.el, [
42652                f.id, f.fieldLabel,
42653                f.labelStyle||this.labelStyle||'',
42654                this.elementStyle||'',
42655                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42656                f.itemCls||this.itemCls||'',
42657                f.width ? f.width + this.padWidth : 160 + this.padWidth
42658        ],true);
42659     }
42660 });
42661  
42662
42663 /**
42664  * @class Roo.form.FieldSet
42665  * @extends Roo.form.Layout
42666  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42667  * @constructor
42668  * @param {Object} config Configuration options
42669  */
42670 Roo.form.FieldSet = function(config){
42671     Roo.form.FieldSet.superclass.constructor.call(this, config);
42672 };
42673
42674 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42675     /**
42676      * @cfg {String} legend
42677      * The text to display as the legend for the FieldSet (defaults to '')
42678      */
42679     /**
42680      * @cfg {String/Object} autoCreate
42681      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42682      */
42683
42684     // private
42685     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42686
42687     // private
42688     onRender : function(ct, position){
42689         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42690         if(this.legend){
42691             this.setLegend(this.legend);
42692         }
42693     },
42694
42695     // private
42696     setLegend : function(text){
42697         if(this.rendered){
42698             this.el.child('legend').update(text);
42699         }
42700     }
42701 });/*
42702  * Based on:
42703  * Ext JS Library 1.1.1
42704  * Copyright(c) 2006-2007, Ext JS, LLC.
42705  *
42706  * Originally Released Under LGPL - original licence link has changed is not relivant.
42707  *
42708  * Fork - LGPL
42709  * <script type="text/javascript">
42710  */
42711 /**
42712  * @class Roo.form.VTypes
42713  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42714  * @singleton
42715  */
42716 Roo.form.VTypes = function(){
42717     // closure these in so they are only created once.
42718     var alpha = /^[a-zA-Z_]+$/;
42719     var alphanum = /^[a-zA-Z0-9_]+$/;
42720     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42721     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42722
42723     // All these messages and functions are configurable
42724     return {
42725         /**
42726          * The function used to validate email addresses
42727          * @param {String} value The email address
42728          */
42729         'email' : function(v){
42730             return email.test(v);
42731         },
42732         /**
42733          * The error text to display when the email validation function returns false
42734          * @type String
42735          */
42736         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42737         /**
42738          * The keystroke filter mask to be applied on email input
42739          * @type RegExp
42740          */
42741         'emailMask' : /[a-z0-9_\.\-@]/i,
42742
42743         /**
42744          * The function used to validate URLs
42745          * @param {String} value The URL
42746          */
42747         'url' : function(v){
42748             return url.test(v);
42749         },
42750         /**
42751          * The error text to display when the url validation function returns false
42752          * @type String
42753          */
42754         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42755         
42756         /**
42757          * The function used to validate alpha values
42758          * @param {String} value The value
42759          */
42760         'alpha' : function(v){
42761             return alpha.test(v);
42762         },
42763         /**
42764          * The error text to display when the alpha validation function returns false
42765          * @type String
42766          */
42767         'alphaText' : 'This field should only contain letters and _',
42768         /**
42769          * The keystroke filter mask to be applied on alpha input
42770          * @type RegExp
42771          */
42772         'alphaMask' : /[a-z_]/i,
42773
42774         /**
42775          * The function used to validate alphanumeric values
42776          * @param {String} value The value
42777          */
42778         'alphanum' : function(v){
42779             return alphanum.test(v);
42780         },
42781         /**
42782          * The error text to display when the alphanumeric validation function returns false
42783          * @type String
42784          */
42785         'alphanumText' : 'This field should only contain letters, numbers and _',
42786         /**
42787          * The keystroke filter mask to be applied on alphanumeric input
42788          * @type RegExp
42789          */
42790         'alphanumMask' : /[a-z0-9_]/i
42791     };
42792 }();//<script type="text/javascript">
42793
42794 /**
42795  * @class Roo.form.FCKeditor
42796  * @extends Roo.form.TextArea
42797  * Wrapper around the FCKEditor http://www.fckeditor.net
42798  * @constructor
42799  * Creates a new FCKeditor
42800  * @param {Object} config Configuration options
42801  */
42802 Roo.form.FCKeditor = function(config){
42803     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42804     this.addEvents({
42805          /**
42806          * @event editorinit
42807          * Fired when the editor is initialized - you can add extra handlers here..
42808          * @param {FCKeditor} this
42809          * @param {Object} the FCK object.
42810          */
42811         editorinit : true
42812     });
42813     
42814     
42815 };
42816 Roo.form.FCKeditor.editors = { };
42817 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42818 {
42819     //defaultAutoCreate : {
42820     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42821     //},
42822     // private
42823     /**
42824      * @cfg {Object} fck options - see fck manual for details.
42825      */
42826     fckconfig : false,
42827     
42828     /**
42829      * @cfg {Object} fck toolbar set (Basic or Default)
42830      */
42831     toolbarSet : 'Basic',
42832     /**
42833      * @cfg {Object} fck BasePath
42834      */ 
42835     basePath : '/fckeditor/',
42836     
42837     
42838     frame : false,
42839     
42840     value : '',
42841     
42842    
42843     onRender : function(ct, position)
42844     {
42845         if(!this.el){
42846             this.defaultAutoCreate = {
42847                 tag: "textarea",
42848                 style:"width:300px;height:60px;",
42849                 autocomplete: "off"
42850             };
42851         }
42852         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42853         /*
42854         if(this.grow){
42855             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42856             if(this.preventScrollbars){
42857                 this.el.setStyle("overflow", "hidden");
42858             }
42859             this.el.setHeight(this.growMin);
42860         }
42861         */
42862         //console.log('onrender' + this.getId() );
42863         Roo.form.FCKeditor.editors[this.getId()] = this;
42864          
42865
42866         this.replaceTextarea() ;
42867         
42868     },
42869     
42870     getEditor : function() {
42871         return this.fckEditor;
42872     },
42873     /**
42874      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42875      * @param {Mixed} value The value to set
42876      */
42877     
42878     
42879     setValue : function(value)
42880     {
42881         //console.log('setValue: ' + value);
42882         
42883         if(typeof(value) == 'undefined') { // not sure why this is happending...
42884             return;
42885         }
42886         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42887         
42888         //if(!this.el || !this.getEditor()) {
42889         //    this.value = value;
42890             //this.setValue.defer(100,this,[value]);    
42891         //    return;
42892         //} 
42893         
42894         if(!this.getEditor()) {
42895             return;
42896         }
42897         
42898         this.getEditor().SetData(value);
42899         
42900         //
42901
42902     },
42903
42904     /**
42905      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42906      * @return {Mixed} value The field value
42907      */
42908     getValue : function()
42909     {
42910         
42911         if (this.frame && this.frame.dom.style.display == 'none') {
42912             return Roo.form.FCKeditor.superclass.getValue.call(this);
42913         }
42914         
42915         if(!this.el || !this.getEditor()) {
42916            
42917            // this.getValue.defer(100,this); 
42918             return this.value;
42919         }
42920        
42921         
42922         var value=this.getEditor().GetData();
42923         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42924         return Roo.form.FCKeditor.superclass.getValue.call(this);
42925         
42926
42927     },
42928
42929     /**
42930      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42931      * @return {Mixed} value The field value
42932      */
42933     getRawValue : function()
42934     {
42935         if (this.frame && this.frame.dom.style.display == 'none') {
42936             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42937         }
42938         
42939         if(!this.el || !this.getEditor()) {
42940             //this.getRawValue.defer(100,this); 
42941             return this.value;
42942             return;
42943         }
42944         
42945         
42946         
42947         var value=this.getEditor().GetData();
42948         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42949         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42950          
42951     },
42952     
42953     setSize : function(w,h) {
42954         
42955         
42956         
42957         //if (this.frame && this.frame.dom.style.display == 'none') {
42958         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42959         //    return;
42960         //}
42961         //if(!this.el || !this.getEditor()) {
42962         //    this.setSize.defer(100,this, [w,h]); 
42963         //    return;
42964         //}
42965         
42966         
42967         
42968         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42969         
42970         this.frame.dom.setAttribute('width', w);
42971         this.frame.dom.setAttribute('height', h);
42972         this.frame.setSize(w,h);
42973         
42974     },
42975     
42976     toggleSourceEdit : function(value) {
42977         
42978       
42979          
42980         this.el.dom.style.display = value ? '' : 'none';
42981         this.frame.dom.style.display = value ?  'none' : '';
42982         
42983     },
42984     
42985     
42986     focus: function(tag)
42987     {
42988         if (this.frame.dom.style.display == 'none') {
42989             return Roo.form.FCKeditor.superclass.focus.call(this);
42990         }
42991         if(!this.el || !this.getEditor()) {
42992             this.focus.defer(100,this, [tag]); 
42993             return;
42994         }
42995         
42996         
42997         
42998         
42999         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43000         this.getEditor().Focus();
43001         if (tgs.length) {
43002             if (!this.getEditor().Selection.GetSelection()) {
43003                 this.focus.defer(100,this, [tag]); 
43004                 return;
43005             }
43006             
43007             
43008             var r = this.getEditor().EditorDocument.createRange();
43009             r.setStart(tgs[0],0);
43010             r.setEnd(tgs[0],0);
43011             this.getEditor().Selection.GetSelection().removeAllRanges();
43012             this.getEditor().Selection.GetSelection().addRange(r);
43013             this.getEditor().Focus();
43014         }
43015         
43016     },
43017     
43018     
43019     
43020     replaceTextarea : function()
43021     {
43022         if ( document.getElementById( this.getId() + '___Frame' ) )
43023             return ;
43024         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43025         //{
43026             // We must check the elements firstly using the Id and then the name.
43027         var oTextarea = document.getElementById( this.getId() );
43028         
43029         var colElementsByName = document.getElementsByName( this.getId() ) ;
43030          
43031         oTextarea.style.display = 'none' ;
43032
43033         if ( oTextarea.tabIndex ) {            
43034             this.TabIndex = oTextarea.tabIndex ;
43035         }
43036         
43037         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43038         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43039         this.frame = Roo.get(this.getId() + '___Frame')
43040     },
43041     
43042     _getConfigHtml : function()
43043     {
43044         var sConfig = '' ;
43045
43046         for ( var o in this.fckconfig ) {
43047             sConfig += sConfig.length > 0  ? '&amp;' : '';
43048             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43049         }
43050
43051         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43052     },
43053     
43054     
43055     _getIFrameHtml : function()
43056     {
43057         var sFile = 'fckeditor.html' ;
43058         /* no idea what this is about..
43059         try
43060         {
43061             if ( (/fcksource=true/i).test( window.top.location.search ) )
43062                 sFile = 'fckeditor.original.html' ;
43063         }
43064         catch (e) { 
43065         */
43066
43067         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43068         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43069         
43070         
43071         var html = '<iframe id="' + this.getId() +
43072             '___Frame" src="' + sLink +
43073             '" width="' + this.width +
43074             '" height="' + this.height + '"' +
43075             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43076             ' frameborder="0" scrolling="no"></iframe>' ;
43077
43078         return html ;
43079     },
43080     
43081     _insertHtmlBefore : function( html, element )
43082     {
43083         if ( element.insertAdjacentHTML )       {
43084             // IE
43085             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43086         } else { // Gecko
43087             var oRange = document.createRange() ;
43088             oRange.setStartBefore( element ) ;
43089             var oFragment = oRange.createContextualFragment( html );
43090             element.parentNode.insertBefore( oFragment, element ) ;
43091         }
43092     }
43093     
43094     
43095   
43096     
43097     
43098     
43099     
43100
43101 });
43102
43103 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43104
43105 function FCKeditor_OnComplete(editorInstance){
43106     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43107     f.fckEditor = editorInstance;
43108     //console.log("loaded");
43109     f.fireEvent('editorinit', f, editorInstance);
43110
43111   
43112
43113  
43114
43115
43116
43117
43118
43119
43120
43121
43122
43123
43124
43125
43126
43127
43128
43129 //<script type="text/javascript">
43130 /**
43131  * @class Roo.form.GridField
43132  * @extends Roo.form.Field
43133  * Embed a grid (or editable grid into a form)
43134  * STATUS ALPHA
43135  * 
43136  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43137  * it needs 
43138  * xgrid.store = Roo.data.Store
43139  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43140  * xgrid.store.reader = Roo.data.JsonReader 
43141  * 
43142  * 
43143  * @constructor
43144  * Creates a new GridField
43145  * @param {Object} config Configuration options
43146  */
43147 Roo.form.GridField = function(config){
43148     Roo.form.GridField.superclass.constructor.call(this, config);
43149      
43150 };
43151
43152 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43153     /**
43154      * @cfg {Number} width  - used to restrict width of grid..
43155      */
43156     width : 100,
43157     /**
43158      * @cfg {Number} height - used to restrict height of grid..
43159      */
43160     height : 50,
43161      /**
43162      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43163          * 
43164          *}
43165      */
43166     xgrid : false, 
43167     /**
43168      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43169      * {tag: "input", type: "checkbox", autocomplete: "off"})
43170      */
43171    // defaultAutoCreate : { tag: 'div' },
43172     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43173     /**
43174      * @cfg {String} addTitle Text to include for adding a title.
43175      */
43176     addTitle : false,
43177     //
43178     onResize : function(){
43179         Roo.form.Field.superclass.onResize.apply(this, arguments);
43180     },
43181
43182     initEvents : function(){
43183         // Roo.form.Checkbox.superclass.initEvents.call(this);
43184         // has no events...
43185        
43186     },
43187
43188
43189     getResizeEl : function(){
43190         return this.wrap;
43191     },
43192
43193     getPositionEl : function(){
43194         return this.wrap;
43195     },
43196
43197     // private
43198     onRender : function(ct, position){
43199         
43200         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43201         var style = this.style;
43202         delete this.style;
43203         
43204         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43205         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43206         this.viewEl = this.wrap.createChild({ tag: 'div' });
43207         if (style) {
43208             this.viewEl.applyStyles(style);
43209         }
43210         if (this.width) {
43211             this.viewEl.setWidth(this.width);
43212         }
43213         if (this.height) {
43214             this.viewEl.setHeight(this.height);
43215         }
43216         //if(this.inputValue !== undefined){
43217         //this.setValue(this.value);
43218         
43219         
43220         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43221         
43222         
43223         this.grid.render();
43224         this.grid.getDataSource().on('remove', this.refreshValue, this);
43225         this.grid.getDataSource().on('update', this.refreshValue, this);
43226         this.grid.on('afteredit', this.refreshValue, this);
43227  
43228     },
43229      
43230     
43231     /**
43232      * Sets the value of the item. 
43233      * @param {String} either an object  or a string..
43234      */
43235     setValue : function(v){
43236         //this.value = v;
43237         v = v || []; // empty set..
43238         // this does not seem smart - it really only affects memoryproxy grids..
43239         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43240             var ds = this.grid.getDataSource();
43241             // assumes a json reader..
43242             var data = {}
43243             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43244             ds.loadData( data);
43245         }
43246         // clear selection so it does not get stale.
43247         if (this.grid.sm) { 
43248             this.grid.sm.clearSelections();
43249         }
43250         
43251         Roo.form.GridField.superclass.setValue.call(this, v);
43252         this.refreshValue();
43253         // should load data in the grid really....
43254     },
43255     
43256     // private
43257     refreshValue: function() {
43258          var val = [];
43259         this.grid.getDataSource().each(function(r) {
43260             val.push(r.data);
43261         });
43262         this.el.dom.value = Roo.encode(val);
43263     }
43264     
43265      
43266     
43267     
43268 });/*
43269  * Based on:
43270  * Ext JS Library 1.1.1
43271  * Copyright(c) 2006-2007, Ext JS, LLC.
43272  *
43273  * Originally Released Under LGPL - original licence link has changed is not relivant.
43274  *
43275  * Fork - LGPL
43276  * <script type="text/javascript">
43277  */
43278 /**
43279  * @class Roo.form.DisplayField
43280  * @extends Roo.form.Field
43281  * A generic Field to display non-editable data.
43282  * @constructor
43283  * Creates a new Display Field item.
43284  * @param {Object} config Configuration options
43285  */
43286 Roo.form.DisplayField = function(config){
43287     Roo.form.DisplayField.superclass.constructor.call(this, config);
43288     
43289 };
43290
43291 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43292     inputType:      'hidden',
43293     allowBlank:     true,
43294     readOnly:         true,
43295     
43296  
43297     /**
43298      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43299      */
43300     focusClass : undefined,
43301     /**
43302      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43303      */
43304     fieldClass: 'x-form-field',
43305     
43306      /**
43307      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43308      */
43309     valueRenderer: undefined,
43310     
43311     width: 100,
43312     /**
43313      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43314      * {tag: "input", type: "checkbox", autocomplete: "off"})
43315      */
43316      
43317  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43318
43319     onResize : function(){
43320         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43321         
43322     },
43323
43324     initEvents : function(){
43325         // Roo.form.Checkbox.superclass.initEvents.call(this);
43326         // has no events...
43327        
43328     },
43329
43330
43331     getResizeEl : function(){
43332         return this.wrap;
43333     },
43334
43335     getPositionEl : function(){
43336         return this.wrap;
43337     },
43338
43339     // private
43340     onRender : function(ct, position){
43341         
43342         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43343         //if(this.inputValue !== undefined){
43344         this.wrap = this.el.wrap();
43345         
43346         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43347         
43348         if (this.bodyStyle) {
43349             this.viewEl.applyStyles(this.bodyStyle);
43350         }
43351         //this.viewEl.setStyle('padding', '2px');
43352         
43353         this.setValue(this.value);
43354         
43355     },
43356 /*
43357     // private
43358     initValue : Roo.emptyFn,
43359
43360   */
43361
43362         // private
43363     onClick : function(){
43364         
43365     },
43366
43367     /**
43368      * Sets the checked state of the checkbox.
43369      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43370      */
43371     setValue : function(v){
43372         this.value = v;
43373         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43374         // this might be called before we have a dom element..
43375         if (!this.viewEl) {
43376             return;
43377         }
43378         this.viewEl.dom.innerHTML = html;
43379         Roo.form.DisplayField.superclass.setValue.call(this, v);
43380
43381     }
43382 });/*
43383  * 
43384  * Licence- LGPL
43385  * 
43386  */
43387
43388 /**
43389  * @class Roo.form.DayPicker
43390  * @extends Roo.form.Field
43391  * A Day picker show [M] [T] [W] ....
43392  * @constructor
43393  * Creates a new Day Picker
43394  * @param {Object} config Configuration options
43395  */
43396 Roo.form.DayPicker= function(config){
43397     Roo.form.DayPicker.superclass.constructor.call(this, config);
43398      
43399 };
43400
43401 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43402     /**
43403      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43404      */
43405     focusClass : undefined,
43406     /**
43407      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43408      */
43409     fieldClass: "x-form-field",
43410    
43411     /**
43412      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43413      * {tag: "input", type: "checkbox", autocomplete: "off"})
43414      */
43415     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43416     
43417    
43418     actionMode : 'viewEl', 
43419     //
43420     // private
43421  
43422     inputType : 'hidden',
43423     
43424      
43425     inputElement: false, // real input element?
43426     basedOn: false, // ????
43427     
43428     isFormField: true, // not sure where this is needed!!!!
43429
43430     onResize : function(){
43431         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43432         if(!this.boxLabel){
43433             this.el.alignTo(this.wrap, 'c-c');
43434         }
43435     },
43436
43437     initEvents : function(){
43438         Roo.form.Checkbox.superclass.initEvents.call(this);
43439         this.el.on("click", this.onClick,  this);
43440         this.el.on("change", this.onClick,  this);
43441     },
43442
43443
43444     getResizeEl : function(){
43445         return this.wrap;
43446     },
43447
43448     getPositionEl : function(){
43449         return this.wrap;
43450     },
43451
43452     
43453     // private
43454     onRender : function(ct, position){
43455         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43456        
43457         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43458         
43459         var r1 = '<table><tr>';
43460         var r2 = '<tr class="x-form-daypick-icons">';
43461         for (var i=0; i < 7; i++) {
43462             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43463             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43464         }
43465         
43466         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43467         viewEl.select('img').on('click', this.onClick, this);
43468         this.viewEl = viewEl;   
43469         
43470         
43471         // this will not work on Chrome!!!
43472         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43473         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43474         
43475         
43476           
43477
43478     },
43479
43480     // private
43481     initValue : Roo.emptyFn,
43482
43483     /**
43484      * Returns the checked state of the checkbox.
43485      * @return {Boolean} True if checked, else false
43486      */
43487     getValue : function(){
43488         return this.el.dom.value;
43489         
43490     },
43491
43492         // private
43493     onClick : function(e){ 
43494         //this.setChecked(!this.checked);
43495         Roo.get(e.target).toggleClass('x-menu-item-checked');
43496         this.refreshValue();
43497         //if(this.el.dom.checked != this.checked){
43498         //    this.setValue(this.el.dom.checked);
43499        // }
43500     },
43501     
43502     // private
43503     refreshValue : function()
43504     {
43505         var val = '';
43506         this.viewEl.select('img',true).each(function(e,i,n)  {
43507             val += e.is(".x-menu-item-checked") ? String(n) : '';
43508         });
43509         this.setValue(val, true);
43510     },
43511
43512     /**
43513      * Sets the checked state of the checkbox.
43514      * On is always based on a string comparison between inputValue and the param.
43515      * @param {Boolean/String} value - the value to set 
43516      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43517      */
43518     setValue : function(v,suppressEvent){
43519         if (!this.el.dom) {
43520             return;
43521         }
43522         var old = this.el.dom.value ;
43523         this.el.dom.value = v;
43524         if (suppressEvent) {
43525             return ;
43526         }
43527          
43528         // update display..
43529         this.viewEl.select('img',true).each(function(e,i,n)  {
43530             
43531             var on = e.is(".x-menu-item-checked");
43532             var newv = v.indexOf(String(n)) > -1;
43533             if (on != newv) {
43534                 e.toggleClass('x-menu-item-checked');
43535             }
43536             
43537         });
43538         
43539         
43540         this.fireEvent('change', this, v, old);
43541         
43542         
43543     },
43544    
43545     // handle setting of hidden value by some other method!!?!?
43546     setFromHidden: function()
43547     {
43548         if(!this.el){
43549             return;
43550         }
43551         //console.log("SET FROM HIDDEN");
43552         //alert('setFrom hidden');
43553         this.setValue(this.el.dom.value);
43554     },
43555     
43556     onDestroy : function()
43557     {
43558         if(this.viewEl){
43559             Roo.get(this.viewEl).remove();
43560         }
43561          
43562         Roo.form.DayPicker.superclass.onDestroy.call(this);
43563     }
43564
43565 });/*
43566  * RooJS Library 1.1.1
43567  * Copyright(c) 2008-2011  Alan Knowles
43568  *
43569  * License - LGPL
43570  */
43571  
43572
43573 /**
43574  * @class Roo.form.ComboCheck
43575  * @extends Roo.form.ComboBox
43576  * A combobox for multiple select items.
43577  *
43578  * FIXME - could do with a reset button..
43579  * 
43580  * @constructor
43581  * Create a new ComboCheck
43582  * @param {Object} config Configuration options
43583  */
43584 Roo.form.ComboCheck = function(config){
43585     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43586     // should verify some data...
43587     // like
43588     // hiddenName = required..
43589     // displayField = required
43590     // valudField == required
43591     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43592     var _t = this;
43593     Roo.each(req, function(e) {
43594         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43595             throw "Roo.form.ComboCheck : missing value for: " + e;
43596         }
43597     });
43598     
43599     
43600 };
43601
43602 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43603      
43604      
43605     editable : false,
43606      
43607     selectedClass: 'x-menu-item-checked', 
43608     
43609     // private
43610     onRender : function(ct, position){
43611         var _t = this;
43612         
43613         
43614         
43615         if(!this.tpl){
43616             var cls = 'x-combo-list';
43617
43618             
43619             this.tpl =  new Roo.Template({
43620                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43621                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43622                    '<span>{' + this.displayField + '}</span>' +
43623                     '</div>' 
43624                 
43625             });
43626         }
43627  
43628         
43629         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43630         this.view.singleSelect = false;
43631         this.view.multiSelect = true;
43632         this.view.toggleSelect = true;
43633         this.pageTb.add(new Roo.Toolbar.Fill(), {
43634             
43635             text: 'Done',
43636             handler: function()
43637             {
43638                 _t.collapse();
43639             }
43640         });
43641     },
43642     
43643     onViewOver : function(e, t){
43644         // do nothing...
43645         return;
43646         
43647     },
43648     
43649     onViewClick : function(doFocus,index){
43650         return;
43651         
43652     },
43653     select: function () {
43654         //Roo.log("SELECT CALLED");
43655     },
43656      
43657     selectByValue : function(xv, scrollIntoView){
43658         var ar = this.getValueArray();
43659         var sels = [];
43660         
43661         Roo.each(ar, function(v) {
43662             if(v === undefined || v === null){
43663                 return;
43664             }
43665             var r = this.findRecord(this.valueField, v);
43666             if(r){
43667                 sels.push(this.store.indexOf(r))
43668                 
43669             }
43670         },this);
43671         this.view.select(sels);
43672         return false;
43673     },
43674     
43675     
43676     
43677     onSelect : function(record, index){
43678        // Roo.log("onselect Called");
43679        // this is only called by the clear button now..
43680         this.view.clearSelections();
43681         this.setValue('[]');
43682         if (this.value != this.valueBefore) {
43683             this.fireEvent('change', this, this.value, this.valueBefore);
43684         }
43685     },
43686     getValueArray : function()
43687     {
43688         var ar = [] ;
43689         
43690         try {
43691             Roo.log(this.value);
43692             var ar = Roo.decode(this.value);
43693             return  ar instanceof Array ? ar : []; //?? valid?
43694             
43695         } catch(e) {
43696             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43697             return [];
43698         }
43699          
43700     },
43701     expand : function ()
43702     {
43703         Roo.form.ComboCheck.superclass.expand.call(this);
43704         this.valueBefore = this.value;
43705         
43706
43707     },
43708     
43709     collapse : function(){
43710         Roo.form.ComboCheck.superclass.collapse.call(this);
43711         var sl = this.view.getSelectedIndexes();
43712         var st = this.store;
43713         var nv = [];
43714         var tv = [];
43715         var r;
43716         Roo.each(sl, function(i) {
43717             r = st.getAt(i);
43718             nv.push(r.get(this.valueField));
43719         },this);
43720         this.setValue(Roo.encode(nv));
43721         if (this.value != this.valueBefore) {
43722
43723             this.fireEvent('change', this, this.value, this.valueBefore);
43724         }
43725         
43726     },
43727     
43728     setValue : function(v){
43729         // Roo.log(v);
43730         this.value = v;
43731         
43732         var vals = this.getValueArray();
43733         var tv = [];
43734         Roo.each(vals, function(k) {
43735             var r = this.findRecord(this.valueField, k);
43736             if(r){
43737                 tv.push(r.data[this.displayField]);
43738             }else if(this.valueNotFoundText !== undefined){
43739                 tv.push( this.valueNotFoundText );
43740             }
43741         },this);
43742        // Roo.log(tv);
43743         
43744         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43745         this.hiddenField.value = v;
43746         this.value = v;
43747     }
43748     
43749 });//<script type="text/javasscript">
43750  
43751
43752 /**
43753  * @class Roo.DDView
43754  * A DnD enabled version of Roo.View.
43755  * @param {Element/String} container The Element in which to create the View.
43756  * @param {String} tpl The template string used to create the markup for each element of the View
43757  * @param {Object} config The configuration properties. These include all the config options of
43758  * {@link Roo.View} plus some specific to this class.<br>
43759  * <p>
43760  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43761  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43762  * <p>
43763  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43764 .x-view-drag-insert-above {
43765         border-top:1px dotted #3366cc;
43766 }
43767 .x-view-drag-insert-below {
43768         border-bottom:1px dotted #3366cc;
43769 }
43770 </code></pre>
43771  * 
43772  */
43773  
43774 Roo.DDView = function(container, tpl, config) {
43775     Roo.DDView.superclass.constructor.apply(this, arguments);
43776     this.getEl().setStyle("outline", "0px none");
43777     this.getEl().unselectable();
43778     if (this.dragGroup) {
43779                 this.setDraggable(this.dragGroup.split(","));
43780     }
43781     if (this.dropGroup) {
43782                 this.setDroppable(this.dropGroup.split(","));
43783     }
43784     if (this.deletable) {
43785         this.setDeletable();
43786     }
43787     this.isDirtyFlag = false;
43788         this.addEvents({
43789                 "drop" : true
43790         });
43791 };
43792
43793 Roo.extend(Roo.DDView, Roo.View, {
43794 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43795 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43796 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43797 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43798
43799         isFormField: true,
43800
43801         reset: Roo.emptyFn,
43802         
43803         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43804
43805         validate: function() {
43806                 return true;
43807         },
43808         
43809         destroy: function() {
43810                 this.purgeListeners();
43811                 this.getEl.removeAllListeners();
43812                 this.getEl().remove();
43813                 if (this.dragZone) {
43814                         if (this.dragZone.destroy) {
43815                                 this.dragZone.destroy();
43816                         }
43817                 }
43818                 if (this.dropZone) {
43819                         if (this.dropZone.destroy) {
43820                                 this.dropZone.destroy();
43821                         }
43822                 }
43823         },
43824
43825 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43826         getName: function() {
43827                 return this.name;
43828         },
43829
43830 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43831         setValue: function(v) {
43832                 if (!this.store) {
43833                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43834                 }
43835                 var data = {};
43836                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43837                 this.store.proxy = new Roo.data.MemoryProxy(data);
43838                 this.store.load();
43839         },
43840
43841 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43842         getValue: function() {
43843                 var result = '(';
43844                 this.store.each(function(rec) {
43845                         result += rec.id + ',';
43846                 });
43847                 return result.substr(0, result.length - 1) + ')';
43848         },
43849         
43850         getIds: function() {
43851                 var i = 0, result = new Array(this.store.getCount());
43852                 this.store.each(function(rec) {
43853                         result[i++] = rec.id;
43854                 });
43855                 return result;
43856         },
43857         
43858         isDirty: function() {
43859                 return this.isDirtyFlag;
43860         },
43861
43862 /**
43863  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43864  *      whole Element becomes the target, and this causes the drop gesture to append.
43865  */
43866     getTargetFromEvent : function(e) {
43867                 var target = e.getTarget();
43868                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43869                 target = target.parentNode;
43870                 }
43871                 if (!target) {
43872                         target = this.el.dom.lastChild || this.el.dom;
43873                 }
43874                 return target;
43875     },
43876
43877 /**
43878  *      Create the drag data which consists of an object which has the property "ddel" as
43879  *      the drag proxy element. 
43880  */
43881     getDragData : function(e) {
43882         var target = this.findItemFromChild(e.getTarget());
43883                 if(target) {
43884                         this.handleSelection(e);
43885                         var selNodes = this.getSelectedNodes();
43886             var dragData = {
43887                 source: this,
43888                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43889                 nodes: selNodes,
43890                 records: []
43891                         };
43892                         var selectedIndices = this.getSelectedIndexes();
43893                         for (var i = 0; i < selectedIndices.length; i++) {
43894                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43895                         }
43896                         if (selNodes.length == 1) {
43897                                 dragData.ddel = target.cloneNode(true); // the div element
43898                         } else {
43899                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43900                                 div.className = 'multi-proxy';
43901                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43902                                         div.appendChild(selNodes[i].cloneNode(true));
43903                                 }
43904                                 dragData.ddel = div;
43905                         }
43906             //console.log(dragData)
43907             //console.log(dragData.ddel.innerHTML)
43908                         return dragData;
43909                 }
43910         //console.log('nodragData')
43911                 return false;
43912     },
43913     
43914 /**     Specify to which ddGroup items in this DDView may be dragged. */
43915     setDraggable: function(ddGroup) {
43916         if (ddGroup instanceof Array) {
43917                 Roo.each(ddGroup, this.setDraggable, this);
43918                 return;
43919         }
43920         if (this.dragZone) {
43921                 this.dragZone.addToGroup(ddGroup);
43922         } else {
43923                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43924                                 containerScroll: true,
43925                                 ddGroup: ddGroup 
43926
43927                         });
43928 //                      Draggability implies selection. DragZone's mousedown selects the element.
43929                         if (!this.multiSelect) { this.singleSelect = true; }
43930
43931 //                      Wire the DragZone's handlers up to methods in *this*
43932                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43933                 }
43934     },
43935
43936 /**     Specify from which ddGroup this DDView accepts drops. */
43937     setDroppable: function(ddGroup) {
43938         if (ddGroup instanceof Array) {
43939                 Roo.each(ddGroup, this.setDroppable, this);
43940                 return;
43941         }
43942         if (this.dropZone) {
43943                 this.dropZone.addToGroup(ddGroup);
43944         } else {
43945                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43946                                 containerScroll: true,
43947                                 ddGroup: ddGroup
43948                         });
43949
43950 //                      Wire the DropZone's handlers up to methods in *this*
43951                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43952                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43953                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43954                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43955                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43956                 }
43957     },
43958
43959 /**     Decide whether to drop above or below a View node. */
43960     getDropPoint : function(e, n, dd){
43961         if (n == this.el.dom) { return "above"; }
43962                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43963                 var c = t + (b - t) / 2;
43964                 var y = Roo.lib.Event.getPageY(e);
43965                 if(y <= c) {
43966                         return "above";
43967                 }else{
43968                         return "below";
43969                 }
43970     },
43971
43972     onNodeEnter : function(n, dd, e, data){
43973                 return false;
43974     },
43975     
43976     onNodeOver : function(n, dd, e, data){
43977                 var pt = this.getDropPoint(e, n, dd);
43978                 // set the insert point style on the target node
43979                 var dragElClass = this.dropNotAllowed;
43980                 if (pt) {
43981                         var targetElClass;
43982                         if (pt == "above"){
43983                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43984                                 targetElClass = "x-view-drag-insert-above";
43985                         } else {
43986                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43987                                 targetElClass = "x-view-drag-insert-below";
43988                         }
43989                         if (this.lastInsertClass != targetElClass){
43990                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43991                                 this.lastInsertClass = targetElClass;
43992                         }
43993                 }
43994                 return dragElClass;
43995         },
43996
43997     onNodeOut : function(n, dd, e, data){
43998                 this.removeDropIndicators(n);
43999     },
44000
44001     onNodeDrop : function(n, dd, e, data){
44002         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44003                 return false;
44004         }
44005         var pt = this.getDropPoint(e, n, dd);
44006                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44007                 if (pt == "below") { insertAt++; }
44008                 for (var i = 0; i < data.records.length; i++) {
44009                         var r = data.records[i];
44010                         var dup = this.store.getById(r.id);
44011                         if (dup && (dd != this.dragZone)) {
44012                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44013                         } else {
44014                                 if (data.copy) {
44015                                         this.store.insert(insertAt++, r.copy());
44016                                 } else {
44017                                         data.source.isDirtyFlag = true;
44018                                         r.store.remove(r);
44019                                         this.store.insert(insertAt++, r);
44020                                 }
44021                                 this.isDirtyFlag = true;
44022                         }
44023                 }
44024                 this.dragZone.cachedTarget = null;
44025                 return true;
44026     },
44027
44028     removeDropIndicators : function(n){
44029                 if(n){
44030                         Roo.fly(n).removeClass([
44031                                 "x-view-drag-insert-above",
44032                                 "x-view-drag-insert-below"]);
44033                         this.lastInsertClass = "_noclass";
44034                 }
44035     },
44036
44037 /**
44038  *      Utility method. Add a delete option to the DDView's context menu.
44039  *      @param {String} imageUrl The URL of the "delete" icon image.
44040  */
44041         setDeletable: function(imageUrl) {
44042                 if (!this.singleSelect && !this.multiSelect) {
44043                         this.singleSelect = true;
44044                 }
44045                 var c = this.getContextMenu();
44046                 this.contextMenu.on("itemclick", function(item) {
44047                         switch (item.id) {
44048                                 case "delete":
44049                                         this.remove(this.getSelectedIndexes());
44050                                         break;
44051                         }
44052                 }, this);
44053                 this.contextMenu.add({
44054                         icon: imageUrl,
44055                         id: "delete",
44056                         text: 'Delete'
44057                 });
44058         },
44059         
44060 /**     Return the context menu for this DDView. */
44061         getContextMenu: function() {
44062                 if (!this.contextMenu) {
44063 //                      Create the View's context menu
44064                         this.contextMenu = new Roo.menu.Menu({
44065                                 id: this.id + "-contextmenu"
44066                         });
44067                         this.el.on("contextmenu", this.showContextMenu, this);
44068                 }
44069                 return this.contextMenu;
44070         },
44071         
44072         disableContextMenu: function() {
44073                 if (this.contextMenu) {
44074                         this.el.un("contextmenu", this.showContextMenu, this);
44075                 }
44076         },
44077
44078         showContextMenu: function(e, item) {
44079         item = this.findItemFromChild(e.getTarget());
44080                 if (item) {
44081                         e.stopEvent();
44082                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44083                         this.contextMenu.showAt(e.getXY());
44084             }
44085     },
44086
44087 /**
44088  *      Remove {@link Roo.data.Record}s at the specified indices.
44089  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44090  */
44091     remove: function(selectedIndices) {
44092                 selectedIndices = [].concat(selectedIndices);
44093                 for (var i = 0; i < selectedIndices.length; i++) {
44094                         var rec = this.store.getAt(selectedIndices[i]);
44095                         this.store.remove(rec);
44096                 }
44097     },
44098
44099 /**
44100  *      Double click fires the event, but also, if this is draggable, and there is only one other
44101  *      related DropZone, it transfers the selected node.
44102  */
44103     onDblClick : function(e){
44104         var item = this.findItemFromChild(e.getTarget());
44105         if(item){
44106             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44107                 return false;
44108             }
44109             if (this.dragGroup) {
44110                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44111                     while (targets.indexOf(this.dropZone) > -1) {
44112                             targets.remove(this.dropZone);
44113                                 }
44114                     if (targets.length == 1) {
44115                                         this.dragZone.cachedTarget = null;
44116                         var el = Roo.get(targets[0].getEl());
44117                         var box = el.getBox(true);
44118                         targets[0].onNodeDrop(el.dom, {
44119                                 target: el.dom,
44120                                 xy: [box.x, box.y + box.height - 1]
44121                         }, null, this.getDragData(e));
44122                     }
44123                 }
44124         }
44125     },
44126     
44127     handleSelection: function(e) {
44128                 this.dragZone.cachedTarget = null;
44129         var item = this.findItemFromChild(e.getTarget());
44130         if (!item) {
44131                 this.clearSelections(true);
44132                 return;
44133         }
44134                 if (item && (this.multiSelect || this.singleSelect)){
44135                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44136                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44137                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44138                                 this.unselect(item);
44139                         } else {
44140                                 this.select(item, this.multiSelect && e.ctrlKey);
44141                                 this.lastSelection = item;
44142                         }
44143                 }
44144     },
44145
44146     onItemClick : function(item, index, e){
44147                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44148                         return false;
44149                 }
44150                 return true;
44151     },
44152
44153     unselect : function(nodeInfo, suppressEvent){
44154                 var node = this.getNode(nodeInfo);
44155                 if(node && this.isSelected(node)){
44156                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44157                                 Roo.fly(node).removeClass(this.selectedClass);
44158                                 this.selections.remove(node);
44159                                 if(!suppressEvent){
44160                                         this.fireEvent("selectionchange", this, this.selections);
44161                                 }
44162                         }
44163                 }
44164     }
44165 });
44166 /*
44167  * Based on:
44168  * Ext JS Library 1.1.1
44169  * Copyright(c) 2006-2007, Ext JS, LLC.
44170  *
44171  * Originally Released Under LGPL - original licence link has changed is not relivant.
44172  *
44173  * Fork - LGPL
44174  * <script type="text/javascript">
44175  */
44176  
44177 /**
44178  * @class Roo.LayoutManager
44179  * @extends Roo.util.Observable
44180  * Base class for layout managers.
44181  */
44182 Roo.LayoutManager = function(container, config){
44183     Roo.LayoutManager.superclass.constructor.call(this);
44184     this.el = Roo.get(container);
44185     // ie scrollbar fix
44186     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44187         document.body.scroll = "no";
44188     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44189         this.el.position('relative');
44190     }
44191     this.id = this.el.id;
44192     this.el.addClass("x-layout-container");
44193     /** false to disable window resize monitoring @type Boolean */
44194     this.monitorWindowResize = true;
44195     this.regions = {};
44196     this.addEvents({
44197         /**
44198          * @event layout
44199          * Fires when a layout is performed. 
44200          * @param {Roo.LayoutManager} this
44201          */
44202         "layout" : true,
44203         /**
44204          * @event regionresized
44205          * Fires when the user resizes a region. 
44206          * @param {Roo.LayoutRegion} region The resized region
44207          * @param {Number} newSize The new size (width for east/west, height for north/south)
44208          */
44209         "regionresized" : true,
44210         /**
44211          * @event regioncollapsed
44212          * Fires when a region is collapsed. 
44213          * @param {Roo.LayoutRegion} region The collapsed region
44214          */
44215         "regioncollapsed" : true,
44216         /**
44217          * @event regionexpanded
44218          * Fires when a region is expanded.  
44219          * @param {Roo.LayoutRegion} region The expanded region
44220          */
44221         "regionexpanded" : true
44222     });
44223     this.updating = false;
44224     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44225 };
44226
44227 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44228     /**
44229      * Returns true if this layout is currently being updated
44230      * @return {Boolean}
44231      */
44232     isUpdating : function(){
44233         return this.updating; 
44234     },
44235     
44236     /**
44237      * Suspend the LayoutManager from doing auto-layouts while
44238      * making multiple add or remove calls
44239      */
44240     beginUpdate : function(){
44241         this.updating = true;    
44242     },
44243     
44244     /**
44245      * Restore auto-layouts and optionally disable the manager from performing a layout
44246      * @param {Boolean} noLayout true to disable a layout update 
44247      */
44248     endUpdate : function(noLayout){
44249         this.updating = false;
44250         if(!noLayout){
44251             this.layout();
44252         }    
44253     },
44254     
44255     layout: function(){
44256         
44257     },
44258     
44259     onRegionResized : function(region, newSize){
44260         this.fireEvent("regionresized", region, newSize);
44261         this.layout();
44262     },
44263     
44264     onRegionCollapsed : function(region){
44265         this.fireEvent("regioncollapsed", region);
44266     },
44267     
44268     onRegionExpanded : function(region){
44269         this.fireEvent("regionexpanded", region);
44270     },
44271         
44272     /**
44273      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44274      * performs box-model adjustments.
44275      * @return {Object} The size as an object {width: (the width), height: (the height)}
44276      */
44277     getViewSize : function(){
44278         var size;
44279         if(this.el.dom != document.body){
44280             size = this.el.getSize();
44281         }else{
44282             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44283         }
44284         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44285         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44286         return size;
44287     },
44288     
44289     /**
44290      * Returns the Element this layout is bound to.
44291      * @return {Roo.Element}
44292      */
44293     getEl : function(){
44294         return this.el;
44295     },
44296     
44297     /**
44298      * Returns the specified region.
44299      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44300      * @return {Roo.LayoutRegion}
44301      */
44302     getRegion : function(target){
44303         return this.regions[target.toLowerCase()];
44304     },
44305     
44306     onWindowResize : function(){
44307         if(this.monitorWindowResize){
44308             this.layout();
44309         }
44310     }
44311 });/*
44312  * Based on:
44313  * Ext JS Library 1.1.1
44314  * Copyright(c) 2006-2007, Ext JS, LLC.
44315  *
44316  * Originally Released Under LGPL - original licence link has changed is not relivant.
44317  *
44318  * Fork - LGPL
44319  * <script type="text/javascript">
44320  */
44321 /**
44322  * @class Roo.BorderLayout
44323  * @extends Roo.LayoutManager
44324  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44325  * please see: <br><br>
44326  * <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>
44327  * <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>
44328  * Example:
44329  <pre><code>
44330  var layout = new Roo.BorderLayout(document.body, {
44331     north: {
44332         initialSize: 25,
44333         titlebar: false
44334     },
44335     west: {
44336         split:true,
44337         initialSize: 200,
44338         minSize: 175,
44339         maxSize: 400,
44340         titlebar: true,
44341         collapsible: true
44342     },
44343     east: {
44344         split:true,
44345         initialSize: 202,
44346         minSize: 175,
44347         maxSize: 400,
44348         titlebar: true,
44349         collapsible: true
44350     },
44351     south: {
44352         split:true,
44353         initialSize: 100,
44354         minSize: 100,
44355         maxSize: 200,
44356         titlebar: true,
44357         collapsible: true
44358     },
44359     center: {
44360         titlebar: true,
44361         autoScroll:true,
44362         resizeTabs: true,
44363         minTabWidth: 50,
44364         preferredTabWidth: 150
44365     }
44366 });
44367
44368 // shorthand
44369 var CP = Roo.ContentPanel;
44370
44371 layout.beginUpdate();
44372 layout.add("north", new CP("north", "North"));
44373 layout.add("south", new CP("south", {title: "South", closable: true}));
44374 layout.add("west", new CP("west", {title: "West"}));
44375 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44376 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44377 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44378 layout.getRegion("center").showPanel("center1");
44379 layout.endUpdate();
44380 </code></pre>
44381
44382 <b>The container the layout is rendered into can be either the body element or any other element.
44383 If it is not the body element, the container needs to either be an absolute positioned element,
44384 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44385 the container size if it is not the body element.</b>
44386
44387 * @constructor
44388 * Create a new BorderLayout
44389 * @param {String/HTMLElement/Element} container The container this layout is bound to
44390 * @param {Object} config Configuration options
44391  */
44392 Roo.BorderLayout = function(container, config){
44393     config = config || {};
44394     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44395     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44396     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44397         var target = this.factory.validRegions[i];
44398         if(config[target]){
44399             this.addRegion(target, config[target]);
44400         }
44401     }
44402 };
44403
44404 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44405     /**
44406      * Creates and adds a new region if it doesn't already exist.
44407      * @param {String} target The target region key (north, south, east, west or center).
44408      * @param {Object} config The regions config object
44409      * @return {BorderLayoutRegion} The new region
44410      */
44411     addRegion : function(target, config){
44412         if(!this.regions[target]){
44413             var r = this.factory.create(target, this, config);
44414             this.bindRegion(target, r);
44415         }
44416         return this.regions[target];
44417     },
44418
44419     // private (kinda)
44420     bindRegion : function(name, r){
44421         this.regions[name] = r;
44422         r.on("visibilitychange", this.layout, this);
44423         r.on("paneladded", this.layout, this);
44424         r.on("panelremoved", this.layout, this);
44425         r.on("invalidated", this.layout, this);
44426         r.on("resized", this.onRegionResized, this);
44427         r.on("collapsed", this.onRegionCollapsed, this);
44428         r.on("expanded", this.onRegionExpanded, this);
44429     },
44430
44431     /**
44432      * Performs a layout update.
44433      */
44434     layout : function(){
44435         if(this.updating) return;
44436         var size = this.getViewSize();
44437         var w = size.width;
44438         var h = size.height;
44439         var centerW = w;
44440         var centerH = h;
44441         var centerY = 0;
44442         var centerX = 0;
44443         //var x = 0, y = 0;
44444
44445         var rs = this.regions;
44446         var north = rs["north"];
44447         var south = rs["south"]; 
44448         var west = rs["west"];
44449         var east = rs["east"];
44450         var center = rs["center"];
44451         //if(this.hideOnLayout){ // not supported anymore
44452             //c.el.setStyle("display", "none");
44453         //}
44454         if(north && north.isVisible()){
44455             var b = north.getBox();
44456             var m = north.getMargins();
44457             b.width = w - (m.left+m.right);
44458             b.x = m.left;
44459             b.y = m.top;
44460             centerY = b.height + b.y + m.bottom;
44461             centerH -= centerY;
44462             north.updateBox(this.safeBox(b));
44463         }
44464         if(south && south.isVisible()){
44465             var b = south.getBox();
44466             var m = south.getMargins();
44467             b.width = w - (m.left+m.right);
44468             b.x = m.left;
44469             var totalHeight = (b.height + m.top + m.bottom);
44470             b.y = h - totalHeight + m.top;
44471             centerH -= totalHeight;
44472             south.updateBox(this.safeBox(b));
44473         }
44474         if(west && west.isVisible()){
44475             var b = west.getBox();
44476             var m = west.getMargins();
44477             b.height = centerH - (m.top+m.bottom);
44478             b.x = m.left;
44479             b.y = centerY + m.top;
44480             var totalWidth = (b.width + m.left + m.right);
44481             centerX += totalWidth;
44482             centerW -= totalWidth;
44483             west.updateBox(this.safeBox(b));
44484         }
44485         if(east && east.isVisible()){
44486             var b = east.getBox();
44487             var m = east.getMargins();
44488             b.height = centerH - (m.top+m.bottom);
44489             var totalWidth = (b.width + m.left + m.right);
44490             b.x = w - totalWidth + m.left;
44491             b.y = centerY + m.top;
44492             centerW -= totalWidth;
44493             east.updateBox(this.safeBox(b));
44494         }
44495         if(center){
44496             var m = center.getMargins();
44497             var centerBox = {
44498                 x: centerX + m.left,
44499                 y: centerY + m.top,
44500                 width: centerW - (m.left+m.right),
44501                 height: centerH - (m.top+m.bottom)
44502             };
44503             //if(this.hideOnLayout){
44504                 //center.el.setStyle("display", "block");
44505             //}
44506             center.updateBox(this.safeBox(centerBox));
44507         }
44508         this.el.repaint();
44509         this.fireEvent("layout", this);
44510     },
44511
44512     // private
44513     safeBox : function(box){
44514         box.width = Math.max(0, box.width);
44515         box.height = Math.max(0, box.height);
44516         return box;
44517     },
44518
44519     /**
44520      * Adds a ContentPanel (or subclass) to this layout.
44521      * @param {String} target The target region key (north, south, east, west or center).
44522      * @param {Roo.ContentPanel} panel The panel to add
44523      * @return {Roo.ContentPanel} The added panel
44524      */
44525     add : function(target, panel){
44526          
44527         target = target.toLowerCase();
44528         return this.regions[target].add(panel);
44529     },
44530
44531     /**
44532      * Remove a ContentPanel (or subclass) to this layout.
44533      * @param {String} target The target region key (north, south, east, west or center).
44534      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44535      * @return {Roo.ContentPanel} The removed panel
44536      */
44537     remove : function(target, panel){
44538         target = target.toLowerCase();
44539         return this.regions[target].remove(panel);
44540     },
44541
44542     /**
44543      * Searches all regions for a panel with the specified id
44544      * @param {String} panelId
44545      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44546      */
44547     findPanel : function(panelId){
44548         var rs = this.regions;
44549         for(var target in rs){
44550             if(typeof rs[target] != "function"){
44551                 var p = rs[target].getPanel(panelId);
44552                 if(p){
44553                     return p;
44554                 }
44555             }
44556         }
44557         return null;
44558     },
44559
44560     /**
44561      * Searches all regions for a panel with the specified id and activates (shows) it.
44562      * @param {String/ContentPanel} panelId The panels id or the panel itself
44563      * @return {Roo.ContentPanel} The shown panel or null
44564      */
44565     showPanel : function(panelId) {
44566       var rs = this.regions;
44567       for(var target in rs){
44568          var r = rs[target];
44569          if(typeof r != "function"){
44570             if(r.hasPanel(panelId)){
44571                return r.showPanel(panelId);
44572             }
44573          }
44574       }
44575       return null;
44576    },
44577
44578    /**
44579      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44580      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44581      */
44582     restoreState : function(provider){
44583         if(!provider){
44584             provider = Roo.state.Manager;
44585         }
44586         var sm = new Roo.LayoutStateManager();
44587         sm.init(this, provider);
44588     },
44589
44590     /**
44591      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44592      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44593      * a valid ContentPanel config object.  Example:
44594      * <pre><code>
44595 // Create the main layout
44596 var layout = new Roo.BorderLayout('main-ct', {
44597     west: {
44598         split:true,
44599         minSize: 175,
44600         titlebar: true
44601     },
44602     center: {
44603         title:'Components'
44604     }
44605 }, 'main-ct');
44606
44607 // Create and add multiple ContentPanels at once via configs
44608 layout.batchAdd({
44609    west: {
44610        id: 'source-files',
44611        autoCreate:true,
44612        title:'Ext Source Files',
44613        autoScroll:true,
44614        fitToFrame:true
44615    },
44616    center : {
44617        el: cview,
44618        autoScroll:true,
44619        fitToFrame:true,
44620        toolbar: tb,
44621        resizeEl:'cbody'
44622    }
44623 });
44624 </code></pre>
44625      * @param {Object} regions An object containing ContentPanel configs by region name
44626      */
44627     batchAdd : function(regions){
44628         this.beginUpdate();
44629         for(var rname in regions){
44630             var lr = this.regions[rname];
44631             if(lr){
44632                 this.addTypedPanels(lr, regions[rname]);
44633             }
44634         }
44635         this.endUpdate();
44636     },
44637
44638     // private
44639     addTypedPanels : function(lr, ps){
44640         if(typeof ps == 'string'){
44641             lr.add(new Roo.ContentPanel(ps));
44642         }
44643         else if(ps instanceof Array){
44644             for(var i =0, len = ps.length; i < len; i++){
44645                 this.addTypedPanels(lr, ps[i]);
44646             }
44647         }
44648         else if(!ps.events){ // raw config?
44649             var el = ps.el;
44650             delete ps.el; // prevent conflict
44651             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44652         }
44653         else {  // panel object assumed!
44654             lr.add(ps);
44655         }
44656     },
44657     /**
44658      * Adds a xtype elements to the layout.
44659      * <pre><code>
44660
44661 layout.addxtype({
44662        xtype : 'ContentPanel',
44663        region: 'west',
44664        items: [ .... ]
44665    }
44666 );
44667
44668 layout.addxtype({
44669         xtype : 'NestedLayoutPanel',
44670         region: 'west',
44671         layout: {
44672            center: { },
44673            west: { }   
44674         },
44675         items : [ ... list of content panels or nested layout panels.. ]
44676    }
44677 );
44678 </code></pre>
44679      * @param {Object} cfg Xtype definition of item to add.
44680      */
44681     addxtype : function(cfg)
44682     {
44683         // basically accepts a pannel...
44684         // can accept a layout region..!?!?
44685         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44686         
44687         if (!cfg.xtype.match(/Panel$/)) {
44688             return false;
44689         }
44690         var ret = false;
44691         
44692         if (typeof(cfg.region) == 'undefined') {
44693             Roo.log("Failed to add Panel, region was not set");
44694             Roo.log(cfg);
44695             return false;
44696         }
44697         var region = cfg.region;
44698         delete cfg.region;
44699         
44700           
44701         var xitems = [];
44702         if (cfg.items) {
44703             xitems = cfg.items;
44704             delete cfg.items;
44705         }
44706         
44707         
44708         switch(cfg.xtype) 
44709         {
44710             case 'ContentPanel':  // ContentPanel (el, cfg)
44711             case 'ScrollPanel':  // ContentPanel (el, cfg)
44712                 if(cfg.autoCreate) {
44713                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44714                 } else {
44715                     var el = this.el.createChild();
44716                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44717                 }
44718                 
44719                 this.add(region, ret);
44720                 break;
44721             
44722             
44723             case 'TreePanel': // our new panel!
44724                 cfg.el = this.el.createChild();
44725                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44726                 this.add(region, ret);
44727                 break;
44728             
44729             case 'NestedLayoutPanel': 
44730                 // create a new Layout (which is  a Border Layout...
44731                 var el = this.el.createChild();
44732                 var clayout = cfg.layout;
44733                 delete cfg.layout;
44734                 clayout.items   = clayout.items  || [];
44735                 // replace this exitems with the clayout ones..
44736                 xitems = clayout.items;
44737                  
44738                 
44739                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44740                     cfg.background = false;
44741                 }
44742                 var layout = new Roo.BorderLayout(el, clayout);
44743                 
44744                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44745                 //console.log('adding nested layout panel '  + cfg.toSource());
44746                 this.add(region, ret);
44747                 
44748                 break;
44749                 
44750             case 'GridPanel': 
44751             
44752                 // needs grid and region
44753                 
44754                 //var el = this.getRegion(region).el.createChild();
44755                 var el = this.el.createChild();
44756                 // create the grid first...
44757                 
44758                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44759                 delete cfg.grid;
44760                 if (region == 'center' && this.active ) {
44761                     cfg.background = false;
44762                 }
44763                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44764                 
44765                 this.add(region, ret);
44766                 if (cfg.background) {
44767                     ret.on('activate', function(gp) {
44768                         if (!gp.grid.rendered) {
44769                             gp.grid.render();
44770                         }
44771                     });
44772                 } else {
44773                     grid.render();
44774                 }
44775                 break;
44776            
44777                
44778                 
44779                 
44780             default: 
44781                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44782                 return null;
44783              // GridPanel (grid, cfg)
44784             
44785         }
44786         this.beginUpdate();
44787         // add children..
44788         Roo.each(xitems, function(i)  {
44789             ret.addxtype(i);
44790         });
44791         this.endUpdate();
44792         return ret;
44793         
44794     }
44795 });
44796
44797 /**
44798  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44799  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44800  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44801  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44802  * <pre><code>
44803 // shorthand
44804 var CP = Roo.ContentPanel;
44805
44806 var layout = Roo.BorderLayout.create({
44807     north: {
44808         initialSize: 25,
44809         titlebar: false,
44810         panels: [new CP("north", "North")]
44811     },
44812     west: {
44813         split:true,
44814         initialSize: 200,
44815         minSize: 175,
44816         maxSize: 400,
44817         titlebar: true,
44818         collapsible: true,
44819         panels: [new CP("west", {title: "West"})]
44820     },
44821     east: {
44822         split:true,
44823         initialSize: 202,
44824         minSize: 175,
44825         maxSize: 400,
44826         titlebar: true,
44827         collapsible: true,
44828         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44829     },
44830     south: {
44831         split:true,
44832         initialSize: 100,
44833         minSize: 100,
44834         maxSize: 200,
44835         titlebar: true,
44836         collapsible: true,
44837         panels: [new CP("south", {title: "South", closable: true})]
44838     },
44839     center: {
44840         titlebar: true,
44841         autoScroll:true,
44842         resizeTabs: true,
44843         minTabWidth: 50,
44844         preferredTabWidth: 150,
44845         panels: [
44846             new CP("center1", {title: "Close Me", closable: true}),
44847             new CP("center2", {title: "Center Panel", closable: false})
44848         ]
44849     }
44850 }, document.body);
44851
44852 layout.getRegion("center").showPanel("center1");
44853 </code></pre>
44854  * @param config
44855  * @param targetEl
44856  */
44857 Roo.BorderLayout.create = function(config, targetEl){
44858     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44859     layout.beginUpdate();
44860     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44861     for(var j = 0, jlen = regions.length; j < jlen; j++){
44862         var lr = regions[j];
44863         if(layout.regions[lr] && config[lr].panels){
44864             var r = layout.regions[lr];
44865             var ps = config[lr].panels;
44866             layout.addTypedPanels(r, ps);
44867         }
44868     }
44869     layout.endUpdate();
44870     return layout;
44871 };
44872
44873 // private
44874 Roo.BorderLayout.RegionFactory = {
44875     // private
44876     validRegions : ["north","south","east","west","center"],
44877
44878     // private
44879     create : function(target, mgr, config){
44880         target = target.toLowerCase();
44881         if(config.lightweight || config.basic){
44882             return new Roo.BasicLayoutRegion(mgr, config, target);
44883         }
44884         switch(target){
44885             case "north":
44886                 return new Roo.NorthLayoutRegion(mgr, config);
44887             case "south":
44888                 return new Roo.SouthLayoutRegion(mgr, config);
44889             case "east":
44890                 return new Roo.EastLayoutRegion(mgr, config);
44891             case "west":
44892                 return new Roo.WestLayoutRegion(mgr, config);
44893             case "center":
44894                 return new Roo.CenterLayoutRegion(mgr, config);
44895         }
44896         throw 'Layout region "'+target+'" not supported.';
44897     }
44898 };/*
44899  * Based on:
44900  * Ext JS Library 1.1.1
44901  * Copyright(c) 2006-2007, Ext JS, LLC.
44902  *
44903  * Originally Released Under LGPL - original licence link has changed is not relivant.
44904  *
44905  * Fork - LGPL
44906  * <script type="text/javascript">
44907  */
44908  
44909 /**
44910  * @class Roo.BasicLayoutRegion
44911  * @extends Roo.util.Observable
44912  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44913  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44914  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44915  */
44916 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44917     this.mgr = mgr;
44918     this.position  = pos;
44919     this.events = {
44920         /**
44921          * @scope Roo.BasicLayoutRegion
44922          */
44923         
44924         /**
44925          * @event beforeremove
44926          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44927          * @param {Roo.LayoutRegion} this
44928          * @param {Roo.ContentPanel} panel The panel
44929          * @param {Object} e The cancel event object
44930          */
44931         "beforeremove" : true,
44932         /**
44933          * @event invalidated
44934          * Fires when the layout for this region is changed.
44935          * @param {Roo.LayoutRegion} this
44936          */
44937         "invalidated" : true,
44938         /**
44939          * @event visibilitychange
44940          * Fires when this region is shown or hidden 
44941          * @param {Roo.LayoutRegion} this
44942          * @param {Boolean} visibility true or false
44943          */
44944         "visibilitychange" : true,
44945         /**
44946          * @event paneladded
44947          * Fires when a panel is added. 
44948          * @param {Roo.LayoutRegion} this
44949          * @param {Roo.ContentPanel} panel The panel
44950          */
44951         "paneladded" : true,
44952         /**
44953          * @event panelremoved
44954          * Fires when a panel is removed. 
44955          * @param {Roo.LayoutRegion} this
44956          * @param {Roo.ContentPanel} panel The panel
44957          */
44958         "panelremoved" : true,
44959         /**
44960          * @event collapsed
44961          * Fires when this region is collapsed.
44962          * @param {Roo.LayoutRegion} this
44963          */
44964         "collapsed" : true,
44965         /**
44966          * @event expanded
44967          * Fires when this region is expanded.
44968          * @param {Roo.LayoutRegion} this
44969          */
44970         "expanded" : true,
44971         /**
44972          * @event slideshow
44973          * Fires when this region is slid into view.
44974          * @param {Roo.LayoutRegion} this
44975          */
44976         "slideshow" : true,
44977         /**
44978          * @event slidehide
44979          * Fires when this region slides out of view. 
44980          * @param {Roo.LayoutRegion} this
44981          */
44982         "slidehide" : true,
44983         /**
44984          * @event panelactivated
44985          * Fires when a panel is activated. 
44986          * @param {Roo.LayoutRegion} this
44987          * @param {Roo.ContentPanel} panel The activated panel
44988          */
44989         "panelactivated" : true,
44990         /**
44991          * @event resized
44992          * Fires when the user resizes this region. 
44993          * @param {Roo.LayoutRegion} this
44994          * @param {Number} newSize The new size (width for east/west, height for north/south)
44995          */
44996         "resized" : true
44997     };
44998     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44999     this.panels = new Roo.util.MixedCollection();
45000     this.panels.getKey = this.getPanelId.createDelegate(this);
45001     this.box = null;
45002     this.activePanel = null;
45003     // ensure listeners are added...
45004     
45005     if (config.listeners || config.events) {
45006         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45007             listeners : config.listeners || {},
45008             events : config.events || {}
45009         });
45010     }
45011     
45012     if(skipConfig !== true){
45013         this.applyConfig(config);
45014     }
45015 };
45016
45017 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45018     getPanelId : function(p){
45019         return p.getId();
45020     },
45021     
45022     applyConfig : function(config){
45023         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45024         this.config = config;
45025         
45026     },
45027     
45028     /**
45029      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45030      * the width, for horizontal (north, south) the height.
45031      * @param {Number} newSize The new width or height
45032      */
45033     resizeTo : function(newSize){
45034         var el = this.el ? this.el :
45035                  (this.activePanel ? this.activePanel.getEl() : null);
45036         if(el){
45037             switch(this.position){
45038                 case "east":
45039                 case "west":
45040                     el.setWidth(newSize);
45041                     this.fireEvent("resized", this, newSize);
45042                 break;
45043                 case "north":
45044                 case "south":
45045                     el.setHeight(newSize);
45046                     this.fireEvent("resized", this, newSize);
45047                 break;                
45048             }
45049         }
45050     },
45051     
45052     getBox : function(){
45053         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45054     },
45055     
45056     getMargins : function(){
45057         return this.margins;
45058     },
45059     
45060     updateBox : function(box){
45061         this.box = box;
45062         var el = this.activePanel.getEl();
45063         el.dom.style.left = box.x + "px";
45064         el.dom.style.top = box.y + "px";
45065         this.activePanel.setSize(box.width, box.height);
45066     },
45067     
45068     /**
45069      * Returns the container element for this region.
45070      * @return {Roo.Element}
45071      */
45072     getEl : function(){
45073         return this.activePanel;
45074     },
45075     
45076     /**
45077      * Returns true if this region is currently visible.
45078      * @return {Boolean}
45079      */
45080     isVisible : function(){
45081         return this.activePanel ? true : false;
45082     },
45083     
45084     setActivePanel : function(panel){
45085         panel = this.getPanel(panel);
45086         if(this.activePanel && this.activePanel != panel){
45087             this.activePanel.setActiveState(false);
45088             this.activePanel.getEl().setLeftTop(-10000,-10000);
45089         }
45090         this.activePanel = panel;
45091         panel.setActiveState(true);
45092         if(this.box){
45093             panel.setSize(this.box.width, this.box.height);
45094         }
45095         this.fireEvent("panelactivated", this, panel);
45096         this.fireEvent("invalidated");
45097     },
45098     
45099     /**
45100      * Show the specified panel.
45101      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45102      * @return {Roo.ContentPanel} The shown panel or null
45103      */
45104     showPanel : function(panel){
45105         if(panel = this.getPanel(panel)){
45106             this.setActivePanel(panel);
45107         }
45108         return panel;
45109     },
45110     
45111     /**
45112      * Get the active panel for this region.
45113      * @return {Roo.ContentPanel} The active panel or null
45114      */
45115     getActivePanel : function(){
45116         return this.activePanel;
45117     },
45118     
45119     /**
45120      * Add the passed ContentPanel(s)
45121      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45122      * @return {Roo.ContentPanel} The panel added (if only one was added)
45123      */
45124     add : function(panel){
45125         if(arguments.length > 1){
45126             for(var i = 0, len = arguments.length; i < len; i++) {
45127                 this.add(arguments[i]);
45128             }
45129             return null;
45130         }
45131         if(this.hasPanel(panel)){
45132             this.showPanel(panel);
45133             return panel;
45134         }
45135         var el = panel.getEl();
45136         if(el.dom.parentNode != this.mgr.el.dom){
45137             this.mgr.el.dom.appendChild(el.dom);
45138         }
45139         if(panel.setRegion){
45140             panel.setRegion(this);
45141         }
45142         this.panels.add(panel);
45143         el.setStyle("position", "absolute");
45144         if(!panel.background){
45145             this.setActivePanel(panel);
45146             if(this.config.initialSize && this.panels.getCount()==1){
45147                 this.resizeTo(this.config.initialSize);
45148             }
45149         }
45150         this.fireEvent("paneladded", this, panel);
45151         return panel;
45152     },
45153     
45154     /**
45155      * Returns true if the panel is in this region.
45156      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45157      * @return {Boolean}
45158      */
45159     hasPanel : function(panel){
45160         if(typeof panel == "object"){ // must be panel obj
45161             panel = panel.getId();
45162         }
45163         return this.getPanel(panel) ? true : false;
45164     },
45165     
45166     /**
45167      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45168      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45169      * @param {Boolean} preservePanel Overrides the config preservePanel option
45170      * @return {Roo.ContentPanel} The panel that was removed
45171      */
45172     remove : function(panel, preservePanel){
45173         panel = this.getPanel(panel);
45174         if(!panel){
45175             return null;
45176         }
45177         var e = {};
45178         this.fireEvent("beforeremove", this, panel, e);
45179         if(e.cancel === true){
45180             return null;
45181         }
45182         var panelId = panel.getId();
45183         this.panels.removeKey(panelId);
45184         return panel;
45185     },
45186     
45187     /**
45188      * Returns the panel specified or null if it's not in this region.
45189      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45190      * @return {Roo.ContentPanel}
45191      */
45192     getPanel : function(id){
45193         if(typeof id == "object"){ // must be panel obj
45194             return id;
45195         }
45196         return this.panels.get(id);
45197     },
45198     
45199     /**
45200      * Returns this regions position (north/south/east/west/center).
45201      * @return {String} 
45202      */
45203     getPosition: function(){
45204         return this.position;    
45205     }
45206 });/*
45207  * Based on:
45208  * Ext JS Library 1.1.1
45209  * Copyright(c) 2006-2007, Ext JS, LLC.
45210  *
45211  * Originally Released Under LGPL - original licence link has changed is not relivant.
45212  *
45213  * Fork - LGPL
45214  * <script type="text/javascript">
45215  */
45216  
45217 /**
45218  * @class Roo.LayoutRegion
45219  * @extends Roo.BasicLayoutRegion
45220  * This class represents a region in a layout manager.
45221  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45222  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45223  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45224  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45225  * @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})
45226  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45227  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45228  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45229  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45230  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45231  * @cfg {String}    title           The title for the region (overrides panel titles)
45232  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45233  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45234  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45235  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45236  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45237  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45238  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45239  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45240  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45241  * @cfg {Boolean}   showPin         True to show a pin button
45242  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45243  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45244  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45245  * @cfg {Number}    width           For East/West panels
45246  * @cfg {Number}    height          For North/South panels
45247  * @cfg {Boolean}   split           To show the splitter
45248  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45249  */
45250 Roo.LayoutRegion = function(mgr, config, pos){
45251     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45252     var dh = Roo.DomHelper;
45253     /** This region's container element 
45254     * @type Roo.Element */
45255     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45256     /** This region's title element 
45257     * @type Roo.Element */
45258
45259     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45260         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45261         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45262     ]}, true);
45263     this.titleEl.enableDisplayMode();
45264     /** This region's title text element 
45265     * @type HTMLElement */
45266     this.titleTextEl = this.titleEl.dom.firstChild;
45267     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45268     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45269     this.closeBtn.enableDisplayMode();
45270     this.closeBtn.on("click", this.closeClicked, this);
45271     this.closeBtn.hide();
45272
45273     this.createBody(config);
45274     this.visible = true;
45275     this.collapsed = false;
45276
45277     if(config.hideWhenEmpty){
45278         this.hide();
45279         this.on("paneladded", this.validateVisibility, this);
45280         this.on("panelremoved", this.validateVisibility, this);
45281     }
45282     this.applyConfig(config);
45283 };
45284
45285 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45286
45287     createBody : function(){
45288         /** This region's body element 
45289         * @type Roo.Element */
45290         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45291     },
45292
45293     applyConfig : function(c){
45294         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45295             var dh = Roo.DomHelper;
45296             if(c.titlebar !== false){
45297                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45298                 this.collapseBtn.on("click", this.collapse, this);
45299                 this.collapseBtn.enableDisplayMode();
45300
45301                 if(c.showPin === true || this.showPin){
45302                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45303                     this.stickBtn.enableDisplayMode();
45304                     this.stickBtn.on("click", this.expand, this);
45305                     this.stickBtn.hide();
45306                 }
45307             }
45308             /** This region's collapsed element
45309             * @type Roo.Element */
45310             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45311                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45312             ]}, true);
45313             if(c.floatable !== false){
45314                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45315                this.collapsedEl.on("click", this.collapseClick, this);
45316             }
45317
45318             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45319                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45320                    id: "message", unselectable: "on", style:{"float":"left"}});
45321                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45322              }
45323             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45324             this.expandBtn.on("click", this.expand, this);
45325         }
45326         if(this.collapseBtn){
45327             this.collapseBtn.setVisible(c.collapsible == true);
45328         }
45329         this.cmargins = c.cmargins || this.cmargins ||
45330                          (this.position == "west" || this.position == "east" ?
45331                              {top: 0, left: 2, right:2, bottom: 0} :
45332                              {top: 2, left: 0, right:0, bottom: 2});
45333         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45334         this.bottomTabs = c.tabPosition != "top";
45335         this.autoScroll = c.autoScroll || false;
45336         if(this.autoScroll){
45337             this.bodyEl.setStyle("overflow", "auto");
45338         }else{
45339             this.bodyEl.setStyle("overflow", "hidden");
45340         }
45341         //if(c.titlebar !== false){
45342             if((!c.titlebar && !c.title) || c.titlebar === false){
45343                 this.titleEl.hide();
45344             }else{
45345                 this.titleEl.show();
45346                 if(c.title){
45347                     this.titleTextEl.innerHTML = c.title;
45348                 }
45349             }
45350         //}
45351         this.duration = c.duration || .30;
45352         this.slideDuration = c.slideDuration || .45;
45353         this.config = c;
45354         if(c.collapsed){
45355             this.collapse(true);
45356         }
45357         if(c.hidden){
45358             this.hide();
45359         }
45360     },
45361     /**
45362      * Returns true if this region is currently visible.
45363      * @return {Boolean}
45364      */
45365     isVisible : function(){
45366         return this.visible;
45367     },
45368
45369     /**
45370      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45371      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45372      */
45373     setCollapsedTitle : function(title){
45374         title = title || "&#160;";
45375         if(this.collapsedTitleTextEl){
45376             this.collapsedTitleTextEl.innerHTML = title;
45377         }
45378     },
45379
45380     getBox : function(){
45381         var b;
45382         if(!this.collapsed){
45383             b = this.el.getBox(false, true);
45384         }else{
45385             b = this.collapsedEl.getBox(false, true);
45386         }
45387         return b;
45388     },
45389
45390     getMargins : function(){
45391         return this.collapsed ? this.cmargins : this.margins;
45392     },
45393
45394     highlight : function(){
45395         this.el.addClass("x-layout-panel-dragover");
45396     },
45397
45398     unhighlight : function(){
45399         this.el.removeClass("x-layout-panel-dragover");
45400     },
45401
45402     updateBox : function(box){
45403         this.box = box;
45404         if(!this.collapsed){
45405             this.el.dom.style.left = box.x + "px";
45406             this.el.dom.style.top = box.y + "px";
45407             this.updateBody(box.width, box.height);
45408         }else{
45409             this.collapsedEl.dom.style.left = box.x + "px";
45410             this.collapsedEl.dom.style.top = box.y + "px";
45411             this.collapsedEl.setSize(box.width, box.height);
45412         }
45413         if(this.tabs){
45414             this.tabs.autoSizeTabs();
45415         }
45416     },
45417
45418     updateBody : function(w, h){
45419         if(w !== null){
45420             this.el.setWidth(w);
45421             w -= this.el.getBorderWidth("rl");
45422             if(this.config.adjustments){
45423                 w += this.config.adjustments[0];
45424             }
45425         }
45426         if(h !== null){
45427             this.el.setHeight(h);
45428             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45429             h -= this.el.getBorderWidth("tb");
45430             if(this.config.adjustments){
45431                 h += this.config.adjustments[1];
45432             }
45433             this.bodyEl.setHeight(h);
45434             if(this.tabs){
45435                 h = this.tabs.syncHeight(h);
45436             }
45437         }
45438         if(this.panelSize){
45439             w = w !== null ? w : this.panelSize.width;
45440             h = h !== null ? h : this.panelSize.height;
45441         }
45442         if(this.activePanel){
45443             var el = this.activePanel.getEl();
45444             w = w !== null ? w : el.getWidth();
45445             h = h !== null ? h : el.getHeight();
45446             this.panelSize = {width: w, height: h};
45447             this.activePanel.setSize(w, h);
45448         }
45449         if(Roo.isIE && this.tabs){
45450             this.tabs.el.repaint();
45451         }
45452     },
45453
45454     /**
45455      * Returns the container element for this region.
45456      * @return {Roo.Element}
45457      */
45458     getEl : function(){
45459         return this.el;
45460     },
45461
45462     /**
45463      * Hides this region.
45464      */
45465     hide : function(){
45466         if(!this.collapsed){
45467             this.el.dom.style.left = "-2000px";
45468             this.el.hide();
45469         }else{
45470             this.collapsedEl.dom.style.left = "-2000px";
45471             this.collapsedEl.hide();
45472         }
45473         this.visible = false;
45474         this.fireEvent("visibilitychange", this, false);
45475     },
45476
45477     /**
45478      * Shows this region if it was previously hidden.
45479      */
45480     show : function(){
45481         if(!this.collapsed){
45482             this.el.show();
45483         }else{
45484             this.collapsedEl.show();
45485         }
45486         this.visible = true;
45487         this.fireEvent("visibilitychange", this, true);
45488     },
45489
45490     closeClicked : function(){
45491         if(this.activePanel){
45492             this.remove(this.activePanel);
45493         }
45494     },
45495
45496     collapseClick : function(e){
45497         if(this.isSlid){
45498            e.stopPropagation();
45499            this.slideIn();
45500         }else{
45501            e.stopPropagation();
45502            this.slideOut();
45503         }
45504     },
45505
45506     /**
45507      * Collapses this region.
45508      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45509      */
45510     collapse : function(skipAnim){
45511         if(this.collapsed) return;
45512         this.collapsed = true;
45513         if(this.split){
45514             this.split.el.hide();
45515         }
45516         if(this.config.animate && skipAnim !== true){
45517             this.fireEvent("invalidated", this);
45518             this.animateCollapse();
45519         }else{
45520             this.el.setLocation(-20000,-20000);
45521             this.el.hide();
45522             this.collapsedEl.show();
45523             this.fireEvent("collapsed", this);
45524             this.fireEvent("invalidated", this);
45525         }
45526     },
45527
45528     animateCollapse : function(){
45529         // overridden
45530     },
45531
45532     /**
45533      * Expands this region if it was previously collapsed.
45534      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45535      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45536      */
45537     expand : function(e, skipAnim){
45538         if(e) e.stopPropagation();
45539         if(!this.collapsed || this.el.hasActiveFx()) return;
45540         if(this.isSlid){
45541             this.afterSlideIn();
45542             skipAnim = true;
45543         }
45544         this.collapsed = false;
45545         if(this.config.animate && skipAnim !== true){
45546             this.animateExpand();
45547         }else{
45548             this.el.show();
45549             if(this.split){
45550                 this.split.el.show();
45551             }
45552             this.collapsedEl.setLocation(-2000,-2000);
45553             this.collapsedEl.hide();
45554             this.fireEvent("invalidated", this);
45555             this.fireEvent("expanded", this);
45556         }
45557     },
45558
45559     animateExpand : function(){
45560         // overridden
45561     },
45562
45563     initTabs : function()
45564     {
45565         this.bodyEl.setStyle("overflow", "hidden");
45566         var ts = new Roo.TabPanel(
45567                 this.bodyEl.dom,
45568                 {
45569                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45570                     disableTooltips: this.config.disableTabTips,
45571                     toolbar : this.config.toolbar
45572                 }
45573         );
45574         if(this.config.hideTabs){
45575             ts.stripWrap.setDisplayed(false);
45576         }
45577         this.tabs = ts;
45578         ts.resizeTabs = this.config.resizeTabs === true;
45579         ts.minTabWidth = this.config.minTabWidth || 40;
45580         ts.maxTabWidth = this.config.maxTabWidth || 250;
45581         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45582         ts.monitorResize = false;
45583         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45584         ts.bodyEl.addClass('x-layout-tabs-body');
45585         this.panels.each(this.initPanelAsTab, this);
45586     },
45587
45588     initPanelAsTab : function(panel){
45589         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45590                     this.config.closeOnTab && panel.isClosable());
45591         if(panel.tabTip !== undefined){
45592             ti.setTooltip(panel.tabTip);
45593         }
45594         ti.on("activate", function(){
45595               this.setActivePanel(panel);
45596         }, this);
45597         if(this.config.closeOnTab){
45598             ti.on("beforeclose", function(t, e){
45599                 e.cancel = true;
45600                 this.remove(panel);
45601             }, this);
45602         }
45603         return ti;
45604     },
45605
45606     updatePanelTitle : function(panel, title){
45607         if(this.activePanel == panel){
45608             this.updateTitle(title);
45609         }
45610         if(this.tabs){
45611             var ti = this.tabs.getTab(panel.getEl().id);
45612             ti.setText(title);
45613             if(panel.tabTip !== undefined){
45614                 ti.setTooltip(panel.tabTip);
45615             }
45616         }
45617     },
45618
45619     updateTitle : function(title){
45620         if(this.titleTextEl && !this.config.title){
45621             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45622         }
45623     },
45624
45625     setActivePanel : function(panel){
45626         panel = this.getPanel(panel);
45627         if(this.activePanel && this.activePanel != panel){
45628             this.activePanel.setActiveState(false);
45629         }
45630         this.activePanel = panel;
45631         panel.setActiveState(true);
45632         if(this.panelSize){
45633             panel.setSize(this.panelSize.width, this.panelSize.height);
45634         }
45635         if(this.closeBtn){
45636             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45637         }
45638         this.updateTitle(panel.getTitle());
45639         if(this.tabs){
45640             this.fireEvent("invalidated", this);
45641         }
45642         this.fireEvent("panelactivated", this, panel);
45643     },
45644
45645     /**
45646      * Shows the specified panel.
45647      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45648      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45649      */
45650     showPanel : function(panel){
45651         if(panel = this.getPanel(panel)){
45652             if(this.tabs){
45653                 var tab = this.tabs.getTab(panel.getEl().id);
45654                 if(tab.isHidden()){
45655                     this.tabs.unhideTab(tab.id);
45656                 }
45657                 tab.activate();
45658             }else{
45659                 this.setActivePanel(panel);
45660             }
45661         }
45662         return panel;
45663     },
45664
45665     /**
45666      * Get the active panel for this region.
45667      * @return {Roo.ContentPanel} The active panel or null
45668      */
45669     getActivePanel : function(){
45670         return this.activePanel;
45671     },
45672
45673     validateVisibility : function(){
45674         if(this.panels.getCount() < 1){
45675             this.updateTitle("&#160;");
45676             this.closeBtn.hide();
45677             this.hide();
45678         }else{
45679             if(!this.isVisible()){
45680                 this.show();
45681             }
45682         }
45683     },
45684
45685     /**
45686      * Adds the passed ContentPanel(s) to this region.
45687      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45688      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45689      */
45690     add : function(panel){
45691         if(arguments.length > 1){
45692             for(var i = 0, len = arguments.length; i < len; i++) {
45693                 this.add(arguments[i]);
45694             }
45695             return null;
45696         }
45697         if(this.hasPanel(panel)){
45698             this.showPanel(panel);
45699             return panel;
45700         }
45701         panel.setRegion(this);
45702         this.panels.add(panel);
45703         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45704             this.bodyEl.dom.appendChild(panel.getEl().dom);
45705             if(panel.background !== true){
45706                 this.setActivePanel(panel);
45707             }
45708             this.fireEvent("paneladded", this, panel);
45709             return panel;
45710         }
45711         if(!this.tabs){
45712             this.initTabs();
45713         }else{
45714             this.initPanelAsTab(panel);
45715         }
45716         if(panel.background !== true){
45717             this.tabs.activate(panel.getEl().id);
45718         }
45719         this.fireEvent("paneladded", this, panel);
45720         return panel;
45721     },
45722
45723     /**
45724      * Hides the tab for the specified panel.
45725      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45726      */
45727     hidePanel : function(panel){
45728         if(this.tabs && (panel = this.getPanel(panel))){
45729             this.tabs.hideTab(panel.getEl().id);
45730         }
45731     },
45732
45733     /**
45734      * Unhides the tab for a previously hidden panel.
45735      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45736      */
45737     unhidePanel : function(panel){
45738         if(this.tabs && (panel = this.getPanel(panel))){
45739             this.tabs.unhideTab(panel.getEl().id);
45740         }
45741     },
45742
45743     clearPanels : function(){
45744         while(this.panels.getCount() > 0){
45745              this.remove(this.panels.first());
45746         }
45747     },
45748
45749     /**
45750      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45751      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45752      * @param {Boolean} preservePanel Overrides the config preservePanel option
45753      * @return {Roo.ContentPanel} The panel that was removed
45754      */
45755     remove : function(panel, preservePanel){
45756         panel = this.getPanel(panel);
45757         if(!panel){
45758             return null;
45759         }
45760         var e = {};
45761         this.fireEvent("beforeremove", this, panel, e);
45762         if(e.cancel === true){
45763             return null;
45764         }
45765         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45766         var panelId = panel.getId();
45767         this.panels.removeKey(panelId);
45768         if(preservePanel){
45769             document.body.appendChild(panel.getEl().dom);
45770         }
45771         if(this.tabs){
45772             this.tabs.removeTab(panel.getEl().id);
45773         }else if (!preservePanel){
45774             this.bodyEl.dom.removeChild(panel.getEl().dom);
45775         }
45776         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45777             var p = this.panels.first();
45778             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45779             tempEl.appendChild(p.getEl().dom);
45780             this.bodyEl.update("");
45781             this.bodyEl.dom.appendChild(p.getEl().dom);
45782             tempEl = null;
45783             this.updateTitle(p.getTitle());
45784             this.tabs = null;
45785             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45786             this.setActivePanel(p);
45787         }
45788         panel.setRegion(null);
45789         if(this.activePanel == panel){
45790             this.activePanel = null;
45791         }
45792         if(this.config.autoDestroy !== false && preservePanel !== true){
45793             try{panel.destroy();}catch(e){}
45794         }
45795         this.fireEvent("panelremoved", this, panel);
45796         return panel;
45797     },
45798
45799     /**
45800      * Returns the TabPanel component used by this region
45801      * @return {Roo.TabPanel}
45802      */
45803     getTabs : function(){
45804         return this.tabs;
45805     },
45806
45807     createTool : function(parentEl, className){
45808         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45809             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45810         btn.addClassOnOver("x-layout-tools-button-over");
45811         return btn;
45812     }
45813 });/*
45814  * Based on:
45815  * Ext JS Library 1.1.1
45816  * Copyright(c) 2006-2007, Ext JS, LLC.
45817  *
45818  * Originally Released Under LGPL - original licence link has changed is not relivant.
45819  *
45820  * Fork - LGPL
45821  * <script type="text/javascript">
45822  */
45823  
45824
45825
45826 /**
45827  * @class Roo.SplitLayoutRegion
45828  * @extends Roo.LayoutRegion
45829  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45830  */
45831 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45832     this.cursor = cursor;
45833     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45834 };
45835
45836 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45837     splitTip : "Drag to resize.",
45838     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45839     useSplitTips : false,
45840
45841     applyConfig : function(config){
45842         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45843         if(config.split){
45844             if(!this.split){
45845                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45846                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45847                 /** The SplitBar for this region 
45848                 * @type Roo.SplitBar */
45849                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45850                 this.split.on("moved", this.onSplitMove, this);
45851                 this.split.useShim = config.useShim === true;
45852                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45853                 if(this.useSplitTips){
45854                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45855                 }
45856                 if(config.collapsible){
45857                     this.split.el.on("dblclick", this.collapse,  this);
45858                 }
45859             }
45860             if(typeof config.minSize != "undefined"){
45861                 this.split.minSize = config.minSize;
45862             }
45863             if(typeof config.maxSize != "undefined"){
45864                 this.split.maxSize = config.maxSize;
45865             }
45866             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45867                 this.hideSplitter();
45868             }
45869         }
45870     },
45871
45872     getHMaxSize : function(){
45873          var cmax = this.config.maxSize || 10000;
45874          var center = this.mgr.getRegion("center");
45875          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45876     },
45877
45878     getVMaxSize : function(){
45879          var cmax = this.config.maxSize || 10000;
45880          var center = this.mgr.getRegion("center");
45881          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45882     },
45883
45884     onSplitMove : function(split, newSize){
45885         this.fireEvent("resized", this, newSize);
45886     },
45887     
45888     /** 
45889      * Returns the {@link Roo.SplitBar} for this region.
45890      * @return {Roo.SplitBar}
45891      */
45892     getSplitBar : function(){
45893         return this.split;
45894     },
45895     
45896     hide : function(){
45897         this.hideSplitter();
45898         Roo.SplitLayoutRegion.superclass.hide.call(this);
45899     },
45900
45901     hideSplitter : function(){
45902         if(this.split){
45903             this.split.el.setLocation(-2000,-2000);
45904             this.split.el.hide();
45905         }
45906     },
45907
45908     show : function(){
45909         if(this.split){
45910             this.split.el.show();
45911         }
45912         Roo.SplitLayoutRegion.superclass.show.call(this);
45913     },
45914     
45915     beforeSlide: function(){
45916         if(Roo.isGecko){// firefox overflow auto bug workaround
45917             this.bodyEl.clip();
45918             if(this.tabs) this.tabs.bodyEl.clip();
45919             if(this.activePanel){
45920                 this.activePanel.getEl().clip();
45921                 
45922                 if(this.activePanel.beforeSlide){
45923                     this.activePanel.beforeSlide();
45924                 }
45925             }
45926         }
45927     },
45928     
45929     afterSlide : function(){
45930         if(Roo.isGecko){// firefox overflow auto bug workaround
45931             this.bodyEl.unclip();
45932             if(this.tabs) this.tabs.bodyEl.unclip();
45933             if(this.activePanel){
45934                 this.activePanel.getEl().unclip();
45935                 if(this.activePanel.afterSlide){
45936                     this.activePanel.afterSlide();
45937                 }
45938             }
45939         }
45940     },
45941
45942     initAutoHide : function(){
45943         if(this.autoHide !== false){
45944             if(!this.autoHideHd){
45945                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45946                 this.autoHideHd = {
45947                     "mouseout": function(e){
45948                         if(!e.within(this.el, true)){
45949                             st.delay(500);
45950                         }
45951                     },
45952                     "mouseover" : function(e){
45953                         st.cancel();
45954                     },
45955                     scope : this
45956                 };
45957             }
45958             this.el.on(this.autoHideHd);
45959         }
45960     },
45961
45962     clearAutoHide : function(){
45963         if(this.autoHide !== false){
45964             this.el.un("mouseout", this.autoHideHd.mouseout);
45965             this.el.un("mouseover", this.autoHideHd.mouseover);
45966         }
45967     },
45968
45969     clearMonitor : function(){
45970         Roo.get(document).un("click", this.slideInIf, this);
45971     },
45972
45973     // these names are backwards but not changed for compat
45974     slideOut : function(){
45975         if(this.isSlid || this.el.hasActiveFx()){
45976             return;
45977         }
45978         this.isSlid = true;
45979         if(this.collapseBtn){
45980             this.collapseBtn.hide();
45981         }
45982         this.closeBtnState = this.closeBtn.getStyle('display');
45983         this.closeBtn.hide();
45984         if(this.stickBtn){
45985             this.stickBtn.show();
45986         }
45987         this.el.show();
45988         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45989         this.beforeSlide();
45990         this.el.setStyle("z-index", 10001);
45991         this.el.slideIn(this.getSlideAnchor(), {
45992             callback: function(){
45993                 this.afterSlide();
45994                 this.initAutoHide();
45995                 Roo.get(document).on("click", this.slideInIf, this);
45996                 this.fireEvent("slideshow", this);
45997             },
45998             scope: this,
45999             block: true
46000         });
46001     },
46002
46003     afterSlideIn : function(){
46004         this.clearAutoHide();
46005         this.isSlid = false;
46006         this.clearMonitor();
46007         this.el.setStyle("z-index", "");
46008         if(this.collapseBtn){
46009             this.collapseBtn.show();
46010         }
46011         this.closeBtn.setStyle('display', this.closeBtnState);
46012         if(this.stickBtn){
46013             this.stickBtn.hide();
46014         }
46015         this.fireEvent("slidehide", this);
46016     },
46017
46018     slideIn : function(cb){
46019         if(!this.isSlid || this.el.hasActiveFx()){
46020             Roo.callback(cb);
46021             return;
46022         }
46023         this.isSlid = false;
46024         this.beforeSlide();
46025         this.el.slideOut(this.getSlideAnchor(), {
46026             callback: function(){
46027                 this.el.setLeftTop(-10000, -10000);
46028                 this.afterSlide();
46029                 this.afterSlideIn();
46030                 Roo.callback(cb);
46031             },
46032             scope: this,
46033             block: true
46034         });
46035     },
46036     
46037     slideInIf : function(e){
46038         if(!e.within(this.el)){
46039             this.slideIn();
46040         }
46041     },
46042
46043     animateCollapse : function(){
46044         this.beforeSlide();
46045         this.el.setStyle("z-index", 20000);
46046         var anchor = this.getSlideAnchor();
46047         this.el.slideOut(anchor, {
46048             callback : function(){
46049                 this.el.setStyle("z-index", "");
46050                 this.collapsedEl.slideIn(anchor, {duration:.3});
46051                 this.afterSlide();
46052                 this.el.setLocation(-10000,-10000);
46053                 this.el.hide();
46054                 this.fireEvent("collapsed", this);
46055             },
46056             scope: this,
46057             block: true
46058         });
46059     },
46060
46061     animateExpand : function(){
46062         this.beforeSlide();
46063         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46064         this.el.setStyle("z-index", 20000);
46065         this.collapsedEl.hide({
46066             duration:.1
46067         });
46068         this.el.slideIn(this.getSlideAnchor(), {
46069             callback : function(){
46070                 this.el.setStyle("z-index", "");
46071                 this.afterSlide();
46072                 if(this.split){
46073                     this.split.el.show();
46074                 }
46075                 this.fireEvent("invalidated", this);
46076                 this.fireEvent("expanded", this);
46077             },
46078             scope: this,
46079             block: true
46080         });
46081     },
46082
46083     anchors : {
46084         "west" : "left",
46085         "east" : "right",
46086         "north" : "top",
46087         "south" : "bottom"
46088     },
46089
46090     sanchors : {
46091         "west" : "l",
46092         "east" : "r",
46093         "north" : "t",
46094         "south" : "b"
46095     },
46096
46097     canchors : {
46098         "west" : "tl-tr",
46099         "east" : "tr-tl",
46100         "north" : "tl-bl",
46101         "south" : "bl-tl"
46102     },
46103
46104     getAnchor : function(){
46105         return this.anchors[this.position];
46106     },
46107
46108     getCollapseAnchor : function(){
46109         return this.canchors[this.position];
46110     },
46111
46112     getSlideAnchor : function(){
46113         return this.sanchors[this.position];
46114     },
46115
46116     getAlignAdj : function(){
46117         var cm = this.cmargins;
46118         switch(this.position){
46119             case "west":
46120                 return [0, 0];
46121             break;
46122             case "east":
46123                 return [0, 0];
46124             break;
46125             case "north":
46126                 return [0, 0];
46127             break;
46128             case "south":
46129                 return [0, 0];
46130             break;
46131         }
46132     },
46133
46134     getExpandAdj : function(){
46135         var c = this.collapsedEl, cm = this.cmargins;
46136         switch(this.position){
46137             case "west":
46138                 return [-(cm.right+c.getWidth()+cm.left), 0];
46139             break;
46140             case "east":
46141                 return [cm.right+c.getWidth()+cm.left, 0];
46142             break;
46143             case "north":
46144                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46145             break;
46146             case "south":
46147                 return [0, cm.top+cm.bottom+c.getHeight()];
46148             break;
46149         }
46150     }
46151 });/*
46152  * Based on:
46153  * Ext JS Library 1.1.1
46154  * Copyright(c) 2006-2007, Ext JS, LLC.
46155  *
46156  * Originally Released Under LGPL - original licence link has changed is not relivant.
46157  *
46158  * Fork - LGPL
46159  * <script type="text/javascript">
46160  */
46161 /*
46162  * These classes are private internal classes
46163  */
46164 Roo.CenterLayoutRegion = function(mgr, config){
46165     Roo.LayoutRegion.call(this, mgr, config, "center");
46166     this.visible = true;
46167     this.minWidth = config.minWidth || 20;
46168     this.minHeight = config.minHeight || 20;
46169 };
46170
46171 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46172     hide : function(){
46173         // center panel can't be hidden
46174     },
46175     
46176     show : function(){
46177         // center panel can't be hidden
46178     },
46179     
46180     getMinWidth: function(){
46181         return this.minWidth;
46182     },
46183     
46184     getMinHeight: function(){
46185         return this.minHeight;
46186     }
46187 });
46188
46189
46190 Roo.NorthLayoutRegion = function(mgr, config){
46191     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46192     if(this.split){
46193         this.split.placement = Roo.SplitBar.TOP;
46194         this.split.orientation = Roo.SplitBar.VERTICAL;
46195         this.split.el.addClass("x-layout-split-v");
46196     }
46197     var size = config.initialSize || config.height;
46198     if(typeof size != "undefined"){
46199         this.el.setHeight(size);
46200     }
46201 };
46202 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46203     orientation: Roo.SplitBar.VERTICAL,
46204     getBox : function(){
46205         if(this.collapsed){
46206             return this.collapsedEl.getBox();
46207         }
46208         var box = this.el.getBox();
46209         if(this.split){
46210             box.height += this.split.el.getHeight();
46211         }
46212         return box;
46213     },
46214     
46215     updateBox : function(box){
46216         if(this.split && !this.collapsed){
46217             box.height -= this.split.el.getHeight();
46218             this.split.el.setLeft(box.x);
46219             this.split.el.setTop(box.y+box.height);
46220             this.split.el.setWidth(box.width);
46221         }
46222         if(this.collapsed){
46223             this.updateBody(box.width, null);
46224         }
46225         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46226     }
46227 });
46228
46229 Roo.SouthLayoutRegion = function(mgr, config){
46230     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46231     if(this.split){
46232         this.split.placement = Roo.SplitBar.BOTTOM;
46233         this.split.orientation = Roo.SplitBar.VERTICAL;
46234         this.split.el.addClass("x-layout-split-v");
46235     }
46236     var size = config.initialSize || config.height;
46237     if(typeof size != "undefined"){
46238         this.el.setHeight(size);
46239     }
46240 };
46241 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46242     orientation: Roo.SplitBar.VERTICAL,
46243     getBox : function(){
46244         if(this.collapsed){
46245             return this.collapsedEl.getBox();
46246         }
46247         var box = this.el.getBox();
46248         if(this.split){
46249             var sh = this.split.el.getHeight();
46250             box.height += sh;
46251             box.y -= sh;
46252         }
46253         return box;
46254     },
46255     
46256     updateBox : function(box){
46257         if(this.split && !this.collapsed){
46258             var sh = this.split.el.getHeight();
46259             box.height -= sh;
46260             box.y += sh;
46261             this.split.el.setLeft(box.x);
46262             this.split.el.setTop(box.y-sh);
46263             this.split.el.setWidth(box.width);
46264         }
46265         if(this.collapsed){
46266             this.updateBody(box.width, null);
46267         }
46268         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46269     }
46270 });
46271
46272 Roo.EastLayoutRegion = function(mgr, config){
46273     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46274     if(this.split){
46275         this.split.placement = Roo.SplitBar.RIGHT;
46276         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46277         this.split.el.addClass("x-layout-split-h");
46278     }
46279     var size = config.initialSize || config.width;
46280     if(typeof size != "undefined"){
46281         this.el.setWidth(size);
46282     }
46283 };
46284 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46285     orientation: Roo.SplitBar.HORIZONTAL,
46286     getBox : function(){
46287         if(this.collapsed){
46288             return this.collapsedEl.getBox();
46289         }
46290         var box = this.el.getBox();
46291         if(this.split){
46292             var sw = this.split.el.getWidth();
46293             box.width += sw;
46294             box.x -= sw;
46295         }
46296         return box;
46297     },
46298
46299     updateBox : function(box){
46300         if(this.split && !this.collapsed){
46301             var sw = this.split.el.getWidth();
46302             box.width -= sw;
46303             this.split.el.setLeft(box.x);
46304             this.split.el.setTop(box.y);
46305             this.split.el.setHeight(box.height);
46306             box.x += sw;
46307         }
46308         if(this.collapsed){
46309             this.updateBody(null, box.height);
46310         }
46311         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46312     }
46313 });
46314
46315 Roo.WestLayoutRegion = function(mgr, config){
46316     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46317     if(this.split){
46318         this.split.placement = Roo.SplitBar.LEFT;
46319         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46320         this.split.el.addClass("x-layout-split-h");
46321     }
46322     var size = config.initialSize || config.width;
46323     if(typeof size != "undefined"){
46324         this.el.setWidth(size);
46325     }
46326 };
46327 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46328     orientation: Roo.SplitBar.HORIZONTAL,
46329     getBox : function(){
46330         if(this.collapsed){
46331             return this.collapsedEl.getBox();
46332         }
46333         var box = this.el.getBox();
46334         if(this.split){
46335             box.width += this.split.el.getWidth();
46336         }
46337         return box;
46338     },
46339     
46340     updateBox : function(box){
46341         if(this.split && !this.collapsed){
46342             var sw = this.split.el.getWidth();
46343             box.width -= sw;
46344             this.split.el.setLeft(box.x+box.width);
46345             this.split.el.setTop(box.y);
46346             this.split.el.setHeight(box.height);
46347         }
46348         if(this.collapsed){
46349             this.updateBody(null, box.height);
46350         }
46351         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46352     }
46353 });
46354 /*
46355  * Based on:
46356  * Ext JS Library 1.1.1
46357  * Copyright(c) 2006-2007, Ext JS, LLC.
46358  *
46359  * Originally Released Under LGPL - original licence link has changed is not relivant.
46360  *
46361  * Fork - LGPL
46362  * <script type="text/javascript">
46363  */
46364  
46365  
46366 /*
46367  * Private internal class for reading and applying state
46368  */
46369 Roo.LayoutStateManager = function(layout){
46370      // default empty state
46371      this.state = {
46372         north: {},
46373         south: {},
46374         east: {},
46375         west: {}       
46376     };
46377 };
46378
46379 Roo.LayoutStateManager.prototype = {
46380     init : function(layout, provider){
46381         this.provider = provider;
46382         var state = provider.get(layout.id+"-layout-state");
46383         if(state){
46384             var wasUpdating = layout.isUpdating();
46385             if(!wasUpdating){
46386                 layout.beginUpdate();
46387             }
46388             for(var key in state){
46389                 if(typeof state[key] != "function"){
46390                     var rstate = state[key];
46391                     var r = layout.getRegion(key);
46392                     if(r && rstate){
46393                         if(rstate.size){
46394                             r.resizeTo(rstate.size);
46395                         }
46396                         if(rstate.collapsed == true){
46397                             r.collapse(true);
46398                         }else{
46399                             r.expand(null, true);
46400                         }
46401                     }
46402                 }
46403             }
46404             if(!wasUpdating){
46405                 layout.endUpdate();
46406             }
46407             this.state = state; 
46408         }
46409         this.layout = layout;
46410         layout.on("regionresized", this.onRegionResized, this);
46411         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46412         layout.on("regionexpanded", this.onRegionExpanded, this);
46413     },
46414     
46415     storeState : function(){
46416         this.provider.set(this.layout.id+"-layout-state", this.state);
46417     },
46418     
46419     onRegionResized : function(region, newSize){
46420         this.state[region.getPosition()].size = newSize;
46421         this.storeState();
46422     },
46423     
46424     onRegionCollapsed : function(region){
46425         this.state[region.getPosition()].collapsed = true;
46426         this.storeState();
46427     },
46428     
46429     onRegionExpanded : function(region){
46430         this.state[region.getPosition()].collapsed = false;
46431         this.storeState();
46432     }
46433 };/*
46434  * Based on:
46435  * Ext JS Library 1.1.1
46436  * Copyright(c) 2006-2007, Ext JS, LLC.
46437  *
46438  * Originally Released Under LGPL - original licence link has changed is not relivant.
46439  *
46440  * Fork - LGPL
46441  * <script type="text/javascript">
46442  */
46443 /**
46444  * @class Roo.ContentPanel
46445  * @extends Roo.util.Observable
46446  * A basic ContentPanel element.
46447  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46448  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46449  * @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
46450  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46451  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46452  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46453  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46454  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46455  * @cfg {String} title          The title for this panel
46456  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46457  * @cfg {String} url            Calls {@link #setUrl} with this value
46458  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46459  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46460  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46461  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46462
46463  * @constructor
46464  * Create a new ContentPanel.
46465  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46466  * @param {String/Object} config A string to set only the title or a config object
46467  * @param {String} content (optional) Set the HTML content for this panel
46468  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46469  */
46470 Roo.ContentPanel = function(el, config, content){
46471     
46472      
46473     /*
46474     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46475         config = el;
46476         el = Roo.id();
46477     }
46478     if (config && config.parentLayout) { 
46479         el = config.parentLayout.el.createChild(); 
46480     }
46481     */
46482     if(el.autoCreate){ // xtype is available if this is called from factory
46483         config = el;
46484         el = Roo.id();
46485     }
46486     this.el = Roo.get(el);
46487     if(!this.el && config && config.autoCreate){
46488         if(typeof config.autoCreate == "object"){
46489             if(!config.autoCreate.id){
46490                 config.autoCreate.id = config.id||el;
46491             }
46492             this.el = Roo.DomHelper.append(document.body,
46493                         config.autoCreate, true);
46494         }else{
46495             this.el = Roo.DomHelper.append(document.body,
46496                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46497         }
46498     }
46499     this.closable = false;
46500     this.loaded = false;
46501     this.active = false;
46502     if(typeof config == "string"){
46503         this.title = config;
46504     }else{
46505         Roo.apply(this, config);
46506     }
46507     
46508     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46509         this.wrapEl = this.el.wrap();
46510         this.toolbar.container = this.el.insertSibling(false, 'before');
46511         this.toolbar = new Roo.Toolbar(this.toolbar);
46512     }
46513     
46514     
46515     
46516     if(this.resizeEl){
46517         this.resizeEl = Roo.get(this.resizeEl, true);
46518     }else{
46519         this.resizeEl = this.el;
46520     }
46521     this.addEvents({
46522         /**
46523          * @event activate
46524          * Fires when this panel is activated. 
46525          * @param {Roo.ContentPanel} this
46526          */
46527         "activate" : true,
46528         /**
46529          * @event deactivate
46530          * Fires when this panel is activated. 
46531          * @param {Roo.ContentPanel} this
46532          */
46533         "deactivate" : true,
46534
46535         /**
46536          * @event resize
46537          * Fires when this panel is resized if fitToFrame is true.
46538          * @param {Roo.ContentPanel} this
46539          * @param {Number} width The width after any component adjustments
46540          * @param {Number} height The height after any component adjustments
46541          */
46542         "resize" : true,
46543         
46544          /**
46545          * @event render
46546          * Fires when this tab is created
46547          * @param {Roo.ContentPanel} this
46548          */
46549         "render" : true
46550         
46551         
46552         
46553     });
46554     if(this.autoScroll){
46555         this.resizeEl.setStyle("overflow", "auto");
46556     } else {
46557         // fix randome scrolling
46558         this.el.on('scroll', function() {
46559             Roo.log('fix random scolling');
46560             this.scrollTo('top',0); 
46561         });
46562     }
46563     content = content || this.content;
46564     if(content){
46565         this.setContent(content);
46566     }
46567     if(config && config.url){
46568         this.setUrl(this.url, this.params, this.loadOnce);
46569     }
46570     
46571     
46572     
46573     Roo.ContentPanel.superclass.constructor.call(this);
46574     
46575     this.fireEvent('render', this);
46576 };
46577
46578 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46579     tabTip:'',
46580     setRegion : function(region){
46581         this.region = region;
46582         if(region){
46583            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46584         }else{
46585            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46586         } 
46587     },
46588     
46589     /**
46590      * Returns the toolbar for this Panel if one was configured. 
46591      * @return {Roo.Toolbar} 
46592      */
46593     getToolbar : function(){
46594         return this.toolbar;
46595     },
46596     
46597     setActiveState : function(active){
46598         this.active = active;
46599         if(!active){
46600             this.fireEvent("deactivate", this);
46601         }else{
46602             this.fireEvent("activate", this);
46603         }
46604     },
46605     /**
46606      * Updates this panel's element
46607      * @param {String} content The new content
46608      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46609     */
46610     setContent : function(content, loadScripts){
46611         this.el.update(content, loadScripts);
46612     },
46613
46614     ignoreResize : function(w, h){
46615         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46616             return true;
46617         }else{
46618             this.lastSize = {width: w, height: h};
46619             return false;
46620         }
46621     },
46622     /**
46623      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46624      * @return {Roo.UpdateManager} The UpdateManager
46625      */
46626     getUpdateManager : function(){
46627         return this.el.getUpdateManager();
46628     },
46629      /**
46630      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46631      * @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:
46632 <pre><code>
46633 panel.load({
46634     url: "your-url.php",
46635     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46636     callback: yourFunction,
46637     scope: yourObject, //(optional scope)
46638     discardUrl: false,
46639     nocache: false,
46640     text: "Loading...",
46641     timeout: 30,
46642     scripts: false
46643 });
46644 </code></pre>
46645      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46646      * 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.
46647      * @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}
46648      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46649      * @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.
46650      * @return {Roo.ContentPanel} this
46651      */
46652     load : function(){
46653         var um = this.el.getUpdateManager();
46654         um.update.apply(um, arguments);
46655         return this;
46656     },
46657
46658
46659     /**
46660      * 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.
46661      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46662      * @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)
46663      * @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)
46664      * @return {Roo.UpdateManager} The UpdateManager
46665      */
46666     setUrl : function(url, params, loadOnce){
46667         if(this.refreshDelegate){
46668             this.removeListener("activate", this.refreshDelegate);
46669         }
46670         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46671         this.on("activate", this.refreshDelegate);
46672         return this.el.getUpdateManager();
46673     },
46674     
46675     _handleRefresh : function(url, params, loadOnce){
46676         if(!loadOnce || !this.loaded){
46677             var updater = this.el.getUpdateManager();
46678             updater.update(url, params, this._setLoaded.createDelegate(this));
46679         }
46680     },
46681     
46682     _setLoaded : function(){
46683         this.loaded = true;
46684     }, 
46685     
46686     /**
46687      * Returns this panel's id
46688      * @return {String} 
46689      */
46690     getId : function(){
46691         return this.el.id;
46692     },
46693     
46694     /** 
46695      * Returns this panel's element - used by regiosn to add.
46696      * @return {Roo.Element} 
46697      */
46698     getEl : function(){
46699         return this.wrapEl || this.el;
46700     },
46701     
46702     adjustForComponents : function(width, height){
46703         if(this.resizeEl != this.el){
46704             width -= this.el.getFrameWidth('lr');
46705             height -= this.el.getFrameWidth('tb');
46706         }
46707         if(this.toolbar){
46708             var te = this.toolbar.getEl();
46709             height -= te.getHeight();
46710             te.setWidth(width);
46711         }
46712         if(this.adjustments){
46713             width += this.adjustments[0];
46714             height += this.adjustments[1];
46715         }
46716         return {"width": width, "height": height};
46717     },
46718     
46719     setSize : function(width, height){
46720         if(this.fitToFrame && !this.ignoreResize(width, height)){
46721             if(this.fitContainer && this.resizeEl != this.el){
46722                 this.el.setSize(width, height);
46723             }
46724             var size = this.adjustForComponents(width, height);
46725             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46726             this.fireEvent('resize', this, size.width, size.height);
46727         }
46728     },
46729     
46730     /**
46731      * Returns this panel's title
46732      * @return {String} 
46733      */
46734     getTitle : function(){
46735         return this.title;
46736     },
46737     
46738     /**
46739      * Set this panel's title
46740      * @param {String} title
46741      */
46742     setTitle : function(title){
46743         this.title = title;
46744         if(this.region){
46745             this.region.updatePanelTitle(this, title);
46746         }
46747     },
46748     
46749     /**
46750      * Returns true is this panel was configured to be closable
46751      * @return {Boolean} 
46752      */
46753     isClosable : function(){
46754         return this.closable;
46755     },
46756     
46757     beforeSlide : function(){
46758         this.el.clip();
46759         this.resizeEl.clip();
46760     },
46761     
46762     afterSlide : function(){
46763         this.el.unclip();
46764         this.resizeEl.unclip();
46765     },
46766     
46767     /**
46768      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46769      *   Will fail silently if the {@link #setUrl} method has not been called.
46770      *   This does not activate the panel, just updates its content.
46771      */
46772     refresh : function(){
46773         if(this.refreshDelegate){
46774            this.loaded = false;
46775            this.refreshDelegate();
46776         }
46777     },
46778     
46779     /**
46780      * Destroys this panel
46781      */
46782     destroy : function(){
46783         this.el.removeAllListeners();
46784         var tempEl = document.createElement("span");
46785         tempEl.appendChild(this.el.dom);
46786         tempEl.innerHTML = "";
46787         this.el.remove();
46788         this.el = null;
46789     },
46790     
46791     /**
46792      * form - if the content panel contains a form - this is a reference to it.
46793      * @type {Roo.form.Form}
46794      */
46795     form : false,
46796     /**
46797      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46798      *    This contains a reference to it.
46799      * @type {Roo.View}
46800      */
46801     view : false,
46802     
46803       /**
46804      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46805      * <pre><code>
46806
46807 layout.addxtype({
46808        xtype : 'Form',
46809        items: [ .... ]
46810    }
46811 );
46812
46813 </code></pre>
46814      * @param {Object} cfg Xtype definition of item to add.
46815      */
46816     
46817     addxtype : function(cfg) {
46818         // add form..
46819         if (cfg.xtype.match(/^Form$/)) {
46820             var el = this.el.createChild();
46821
46822             this.form = new  Roo.form.Form(cfg);
46823             
46824             
46825             if ( this.form.allItems.length) this.form.render(el.dom);
46826             return this.form;
46827         }
46828         // should only have one of theses..
46829         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46830             // views..
46831             cfg.el = this.el.appendChild(document.createElement("div"));
46832             // factory?
46833             
46834             var ret = new Roo.factory(cfg);
46835             ret.render && ret.render(false, ''); // render blank..
46836             this.view = ret;
46837             return ret;
46838         }
46839         return false;
46840     }
46841 });
46842
46843 /**
46844  * @class Roo.GridPanel
46845  * @extends Roo.ContentPanel
46846  * @constructor
46847  * Create a new GridPanel.
46848  * @param {Roo.grid.Grid} grid The grid for this panel
46849  * @param {String/Object} config A string to set only the panel's title, or a config object
46850  */
46851 Roo.GridPanel = function(grid, config){
46852     
46853   
46854     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46855         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46856         
46857     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46858     
46859     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46860     
46861     if(this.toolbar){
46862         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46863     }
46864     // xtype created footer. - not sure if will work as we normally have to render first..
46865     if (this.footer && !this.footer.el && this.footer.xtype) {
46866         
46867         this.footer.container = this.grid.getView().getFooterPanel(true);
46868         this.footer.dataSource = this.grid.dataSource;
46869         this.footer = Roo.factory(this.footer, Roo);
46870         
46871     }
46872     
46873     grid.monitorWindowResize = false; // turn off autosizing
46874     grid.autoHeight = false;
46875     grid.autoWidth = false;
46876     this.grid = grid;
46877     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46878 };
46879
46880 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46881     getId : function(){
46882         return this.grid.id;
46883     },
46884     
46885     /**
46886      * Returns the grid for this panel
46887      * @return {Roo.grid.Grid} 
46888      */
46889     getGrid : function(){
46890         return this.grid;    
46891     },
46892     
46893     setSize : function(width, height){
46894         if(!this.ignoreResize(width, height)){
46895             var grid = this.grid;
46896             var size = this.adjustForComponents(width, height);
46897             grid.getGridEl().setSize(size.width, size.height);
46898             grid.autoSize();
46899         }
46900     },
46901     
46902     beforeSlide : function(){
46903         this.grid.getView().scroller.clip();
46904     },
46905     
46906     afterSlide : function(){
46907         this.grid.getView().scroller.unclip();
46908     },
46909     
46910     destroy : function(){
46911         this.grid.destroy();
46912         delete this.grid;
46913         Roo.GridPanel.superclass.destroy.call(this); 
46914     }
46915 });
46916
46917
46918 /**
46919  * @class Roo.NestedLayoutPanel
46920  * @extends Roo.ContentPanel
46921  * @constructor
46922  * Create a new NestedLayoutPanel.
46923  * 
46924  * 
46925  * @param {Roo.BorderLayout} layout The layout for this panel
46926  * @param {String/Object} config A string to set only the title or a config object
46927  */
46928 Roo.NestedLayoutPanel = function(layout, config)
46929 {
46930     // construct with only one argument..
46931     /* FIXME - implement nicer consturctors
46932     if (layout.layout) {
46933         config = layout;
46934         layout = config.layout;
46935         delete config.layout;
46936     }
46937     if (layout.xtype && !layout.getEl) {
46938         // then layout needs constructing..
46939         layout = Roo.factory(layout, Roo);
46940     }
46941     */
46942     
46943     
46944     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46945     
46946     layout.monitorWindowResize = false; // turn off autosizing
46947     this.layout = layout;
46948     this.layout.getEl().addClass("x-layout-nested-layout");
46949     
46950     
46951     
46952     
46953 };
46954
46955 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46956
46957     setSize : function(width, height){
46958         if(!this.ignoreResize(width, height)){
46959             var size = this.adjustForComponents(width, height);
46960             var el = this.layout.getEl();
46961             el.setSize(size.width, size.height);
46962             var touch = el.dom.offsetWidth;
46963             this.layout.layout();
46964             // ie requires a double layout on the first pass
46965             if(Roo.isIE && !this.initialized){
46966                 this.initialized = true;
46967                 this.layout.layout();
46968             }
46969         }
46970     },
46971     
46972     // activate all subpanels if not currently active..
46973     
46974     setActiveState : function(active){
46975         this.active = active;
46976         if(!active){
46977             this.fireEvent("deactivate", this);
46978             return;
46979         }
46980         
46981         this.fireEvent("activate", this);
46982         // not sure if this should happen before or after..
46983         if (!this.layout) {
46984             return; // should not happen..
46985         }
46986         var reg = false;
46987         for (var r in this.layout.regions) {
46988             reg = this.layout.getRegion(r);
46989             if (reg.getActivePanel()) {
46990                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46991                 reg.setActivePanel(reg.getActivePanel());
46992                 continue;
46993             }
46994             if (!reg.panels.length) {
46995                 continue;
46996             }
46997             reg.showPanel(reg.getPanel(0));
46998         }
46999         
47000         
47001         
47002         
47003     },
47004     
47005     /**
47006      * Returns the nested BorderLayout for this panel
47007      * @return {Roo.BorderLayout} 
47008      */
47009     getLayout : function(){
47010         return this.layout;
47011     },
47012     
47013      /**
47014      * Adds a xtype elements to the layout of the nested panel
47015      * <pre><code>
47016
47017 panel.addxtype({
47018        xtype : 'ContentPanel',
47019        region: 'west',
47020        items: [ .... ]
47021    }
47022 );
47023
47024 panel.addxtype({
47025         xtype : 'NestedLayoutPanel',
47026         region: 'west',
47027         layout: {
47028            center: { },
47029            west: { }   
47030         },
47031         items : [ ... list of content panels or nested layout panels.. ]
47032    }
47033 );
47034 </code></pre>
47035      * @param {Object} cfg Xtype definition of item to add.
47036      */
47037     addxtype : function(cfg) {
47038         return this.layout.addxtype(cfg);
47039     
47040     }
47041 });
47042
47043 Roo.ScrollPanel = function(el, config, content){
47044     config = config || {};
47045     config.fitToFrame = true;
47046     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47047     
47048     this.el.dom.style.overflow = "hidden";
47049     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47050     this.el.removeClass("x-layout-inactive-content");
47051     this.el.on("mousewheel", this.onWheel, this);
47052
47053     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47054     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47055     up.unselectable(); down.unselectable();
47056     up.on("click", this.scrollUp, this);
47057     down.on("click", this.scrollDown, this);
47058     up.addClassOnOver("x-scroller-btn-over");
47059     down.addClassOnOver("x-scroller-btn-over");
47060     up.addClassOnClick("x-scroller-btn-click");
47061     down.addClassOnClick("x-scroller-btn-click");
47062     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47063
47064     this.resizeEl = this.el;
47065     this.el = wrap; this.up = up; this.down = down;
47066 };
47067
47068 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47069     increment : 100,
47070     wheelIncrement : 5,
47071     scrollUp : function(){
47072         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47073     },
47074
47075     scrollDown : function(){
47076         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47077     },
47078
47079     afterScroll : function(){
47080         var el = this.resizeEl;
47081         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47082         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47083         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47084     },
47085
47086     setSize : function(){
47087         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47088         this.afterScroll();
47089     },
47090
47091     onWheel : function(e){
47092         var d = e.getWheelDelta();
47093         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47094         this.afterScroll();
47095         e.stopEvent();
47096     },
47097
47098     setContent : function(content, loadScripts){
47099         this.resizeEl.update(content, loadScripts);
47100     }
47101
47102 });
47103
47104
47105
47106
47107
47108
47109
47110
47111
47112 /**
47113  * @class Roo.TreePanel
47114  * @extends Roo.ContentPanel
47115  * @constructor
47116  * Create a new TreePanel. - defaults to fit/scoll contents.
47117  * @param {String/Object} config A string to set only the panel's title, or a config object
47118  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47119  */
47120 Roo.TreePanel = function(config){
47121     var el = config.el;
47122     var tree = config.tree;
47123     delete config.tree; 
47124     delete config.el; // hopefull!
47125     
47126     // wrapper for IE7 strict & safari scroll issue
47127     
47128     var treeEl = el.createChild();
47129     config.resizeEl = treeEl;
47130     
47131     
47132     
47133     Roo.TreePanel.superclass.constructor.call(this, el, config);
47134  
47135  
47136     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47137     //console.log(tree);
47138     this.on('activate', function()
47139     {
47140         if (this.tree.rendered) {
47141             return;
47142         }
47143         //console.log('render tree');
47144         this.tree.render();
47145     });
47146     
47147     this.on('resize',  function (cp, w, h) {
47148             this.tree.innerCt.setWidth(w);
47149             this.tree.innerCt.setHeight(h);
47150             this.tree.innerCt.setStyle('overflow-y', 'auto');
47151     });
47152
47153         
47154     
47155 };
47156
47157 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47158     fitToFrame : true,
47159     autoScroll : true
47160 });
47161
47162
47163
47164
47165
47166
47167
47168
47169
47170
47171
47172 /*
47173  * Based on:
47174  * Ext JS Library 1.1.1
47175  * Copyright(c) 2006-2007, Ext JS, LLC.
47176  *
47177  * Originally Released Under LGPL - original licence link has changed is not relivant.
47178  *
47179  * Fork - LGPL
47180  * <script type="text/javascript">
47181  */
47182  
47183
47184 /**
47185  * @class Roo.ReaderLayout
47186  * @extends Roo.BorderLayout
47187  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47188  * center region containing two nested regions (a top one for a list view and one for item preview below),
47189  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47190  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47191  * expedites the setup of the overall layout and regions for this common application style.
47192  * Example:
47193  <pre><code>
47194 var reader = new Roo.ReaderLayout();
47195 var CP = Roo.ContentPanel;  // shortcut for adding
47196
47197 reader.beginUpdate();
47198 reader.add("north", new CP("north", "North"));
47199 reader.add("west", new CP("west", {title: "West"}));
47200 reader.add("east", new CP("east", {title: "East"}));
47201
47202 reader.regions.listView.add(new CP("listView", "List"));
47203 reader.regions.preview.add(new CP("preview", "Preview"));
47204 reader.endUpdate();
47205 </code></pre>
47206 * @constructor
47207 * Create a new ReaderLayout
47208 * @param {Object} config Configuration options
47209 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47210 * document.body if omitted)
47211 */
47212 Roo.ReaderLayout = function(config, renderTo){
47213     var c = config || {size:{}};
47214     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47215         north: c.north !== false ? Roo.apply({
47216             split:false,
47217             initialSize: 32,
47218             titlebar: false
47219         }, c.north) : false,
47220         west: c.west !== false ? Roo.apply({
47221             split:true,
47222             initialSize: 200,
47223             minSize: 175,
47224             maxSize: 400,
47225             titlebar: true,
47226             collapsible: true,
47227             animate: true,
47228             margins:{left:5,right:0,bottom:5,top:5},
47229             cmargins:{left:5,right:5,bottom:5,top:5}
47230         }, c.west) : false,
47231         east: c.east !== false ? Roo.apply({
47232             split:true,
47233             initialSize: 200,
47234             minSize: 175,
47235             maxSize: 400,
47236             titlebar: true,
47237             collapsible: true,
47238             animate: true,
47239             margins:{left:0,right:5,bottom:5,top:5},
47240             cmargins:{left:5,right:5,bottom:5,top:5}
47241         }, c.east) : false,
47242         center: Roo.apply({
47243             tabPosition: 'top',
47244             autoScroll:false,
47245             closeOnTab: true,
47246             titlebar:false,
47247             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47248         }, c.center)
47249     });
47250
47251     this.el.addClass('x-reader');
47252
47253     this.beginUpdate();
47254
47255     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47256         south: c.preview !== false ? Roo.apply({
47257             split:true,
47258             initialSize: 200,
47259             minSize: 100,
47260             autoScroll:true,
47261             collapsible:true,
47262             titlebar: true,
47263             cmargins:{top:5,left:0, right:0, bottom:0}
47264         }, c.preview) : false,
47265         center: Roo.apply({
47266             autoScroll:false,
47267             titlebar:false,
47268             minHeight:200
47269         }, c.listView)
47270     });
47271     this.add('center', new Roo.NestedLayoutPanel(inner,
47272             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47273
47274     this.endUpdate();
47275
47276     this.regions.preview = inner.getRegion('south');
47277     this.regions.listView = inner.getRegion('center');
47278 };
47279
47280 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47281  * Based on:
47282  * Ext JS Library 1.1.1
47283  * Copyright(c) 2006-2007, Ext JS, LLC.
47284  *
47285  * Originally Released Under LGPL - original licence link has changed is not relivant.
47286  *
47287  * Fork - LGPL
47288  * <script type="text/javascript">
47289  */
47290  
47291 /**
47292  * @class Roo.grid.Grid
47293  * @extends Roo.util.Observable
47294  * This class represents the primary interface of a component based grid control.
47295  * <br><br>Usage:<pre><code>
47296  var grid = new Roo.grid.Grid("my-container-id", {
47297      ds: myDataStore,
47298      cm: myColModel,
47299      selModel: mySelectionModel,
47300      autoSizeColumns: true,
47301      monitorWindowResize: false,
47302      trackMouseOver: true
47303  });
47304  // set any options
47305  grid.render();
47306  * </code></pre>
47307  * <b>Common Problems:</b><br/>
47308  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47309  * element will correct this<br/>
47310  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47311  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47312  * are unpredictable.<br/>
47313  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47314  * grid to calculate dimensions/offsets.<br/>
47315   * @constructor
47316  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47317  * The container MUST have some type of size defined for the grid to fill. The container will be
47318  * automatically set to position relative if it isn't already.
47319  * @param {Object} config A config object that sets properties on this grid.
47320  */
47321 Roo.grid.Grid = function(container, config){
47322         // initialize the container
47323         this.container = Roo.get(container);
47324         this.container.update("");
47325         this.container.setStyle("overflow", "hidden");
47326     this.container.addClass('x-grid-container');
47327
47328     this.id = this.container.id;
47329
47330     Roo.apply(this, config);
47331     // check and correct shorthanded configs
47332     if(this.ds){
47333         this.dataSource = this.ds;
47334         delete this.ds;
47335     }
47336     if(this.cm){
47337         this.colModel = this.cm;
47338         delete this.cm;
47339     }
47340     if(this.sm){
47341         this.selModel = this.sm;
47342         delete this.sm;
47343     }
47344
47345     if (this.selModel) {
47346         this.selModel = Roo.factory(this.selModel, Roo.grid);
47347         this.sm = this.selModel;
47348         this.sm.xmodule = this.xmodule || false;
47349     }
47350     if (typeof(this.colModel.config) == 'undefined') {
47351         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47352         this.cm = this.colModel;
47353         this.cm.xmodule = this.xmodule || false;
47354     }
47355     if (this.dataSource) {
47356         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47357         this.ds = this.dataSource;
47358         this.ds.xmodule = this.xmodule || false;
47359          
47360     }
47361     
47362     
47363     
47364     if(this.width){
47365         this.container.setWidth(this.width);
47366     }
47367
47368     if(this.height){
47369         this.container.setHeight(this.height);
47370     }
47371     /** @private */
47372         this.addEvents({
47373         // raw events
47374         /**
47375          * @event click
47376          * The raw click event for the entire grid.
47377          * @param {Roo.EventObject} e
47378          */
47379         "click" : true,
47380         /**
47381          * @event dblclick
47382          * The raw dblclick event for the entire grid.
47383          * @param {Roo.EventObject} e
47384          */
47385         "dblclick" : true,
47386         /**
47387          * @event contextmenu
47388          * The raw contextmenu event for the entire grid.
47389          * @param {Roo.EventObject} e
47390          */
47391         "contextmenu" : true,
47392         /**
47393          * @event mousedown
47394          * The raw mousedown event for the entire grid.
47395          * @param {Roo.EventObject} e
47396          */
47397         "mousedown" : true,
47398         /**
47399          * @event mouseup
47400          * The raw mouseup event for the entire grid.
47401          * @param {Roo.EventObject} e
47402          */
47403         "mouseup" : true,
47404         /**
47405          * @event mouseover
47406          * The raw mouseover event for the entire grid.
47407          * @param {Roo.EventObject} e
47408          */
47409         "mouseover" : true,
47410         /**
47411          * @event mouseout
47412          * The raw mouseout event for the entire grid.
47413          * @param {Roo.EventObject} e
47414          */
47415         "mouseout" : true,
47416         /**
47417          * @event keypress
47418          * The raw keypress event for the entire grid.
47419          * @param {Roo.EventObject} e
47420          */
47421         "keypress" : true,
47422         /**
47423          * @event keydown
47424          * The raw keydown event for the entire grid.
47425          * @param {Roo.EventObject} e
47426          */
47427         "keydown" : true,
47428
47429         // custom events
47430
47431         /**
47432          * @event cellclick
47433          * Fires when a cell is clicked
47434          * @param {Grid} this
47435          * @param {Number} rowIndex
47436          * @param {Number} columnIndex
47437          * @param {Roo.EventObject} e
47438          */
47439         "cellclick" : true,
47440         /**
47441          * @event celldblclick
47442          * Fires when a cell is double clicked
47443          * @param {Grid} this
47444          * @param {Number} rowIndex
47445          * @param {Number} columnIndex
47446          * @param {Roo.EventObject} e
47447          */
47448         "celldblclick" : true,
47449         /**
47450          * @event rowclick
47451          * Fires when a row is clicked
47452          * @param {Grid} this
47453          * @param {Number} rowIndex
47454          * @param {Roo.EventObject} e
47455          */
47456         "rowclick" : true,
47457         /**
47458          * @event rowdblclick
47459          * Fires when a row is double clicked
47460          * @param {Grid} this
47461          * @param {Number} rowIndex
47462          * @param {Roo.EventObject} e
47463          */
47464         "rowdblclick" : true,
47465         /**
47466          * @event headerclick
47467          * Fires when a header is clicked
47468          * @param {Grid} this
47469          * @param {Number} columnIndex
47470          * @param {Roo.EventObject} e
47471          */
47472         "headerclick" : true,
47473         /**
47474          * @event headerdblclick
47475          * Fires when a header cell is double clicked
47476          * @param {Grid} this
47477          * @param {Number} columnIndex
47478          * @param {Roo.EventObject} e
47479          */
47480         "headerdblclick" : true,
47481         /**
47482          * @event rowcontextmenu
47483          * Fires when a row is right clicked
47484          * @param {Grid} this
47485          * @param {Number} rowIndex
47486          * @param {Roo.EventObject} e
47487          */
47488         "rowcontextmenu" : true,
47489         /**
47490          * @event cellcontextmenu
47491          * Fires when a cell is right clicked
47492          * @param {Grid} this
47493          * @param {Number} rowIndex
47494          * @param {Number} cellIndex
47495          * @param {Roo.EventObject} e
47496          */
47497          "cellcontextmenu" : true,
47498         /**
47499          * @event headercontextmenu
47500          * Fires when a header is right clicked
47501          * @param {Grid} this
47502          * @param {Number} columnIndex
47503          * @param {Roo.EventObject} e
47504          */
47505         "headercontextmenu" : true,
47506         /**
47507          * @event bodyscroll
47508          * Fires when the body element is scrolled
47509          * @param {Number} scrollLeft
47510          * @param {Number} scrollTop
47511          */
47512         "bodyscroll" : true,
47513         /**
47514          * @event columnresize
47515          * Fires when the user resizes a column
47516          * @param {Number} columnIndex
47517          * @param {Number} newSize
47518          */
47519         "columnresize" : true,
47520         /**
47521          * @event columnmove
47522          * Fires when the user moves a column
47523          * @param {Number} oldIndex
47524          * @param {Number} newIndex
47525          */
47526         "columnmove" : true,
47527         /**
47528          * @event startdrag
47529          * Fires when row(s) start being dragged
47530          * @param {Grid} this
47531          * @param {Roo.GridDD} dd The drag drop object
47532          * @param {event} e The raw browser event
47533          */
47534         "startdrag" : true,
47535         /**
47536          * @event enddrag
47537          * Fires when a drag operation is complete
47538          * @param {Grid} this
47539          * @param {Roo.GridDD} dd The drag drop object
47540          * @param {event} e The raw browser event
47541          */
47542         "enddrag" : true,
47543         /**
47544          * @event dragdrop
47545          * Fires when dragged row(s) are dropped on a valid DD target
47546          * @param {Grid} this
47547          * @param {Roo.GridDD} dd The drag drop object
47548          * @param {String} targetId The target drag drop object
47549          * @param {event} e The raw browser event
47550          */
47551         "dragdrop" : true,
47552         /**
47553          * @event dragover
47554          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47555          * @param {Grid} this
47556          * @param {Roo.GridDD} dd The drag drop object
47557          * @param {String} targetId The target drag drop object
47558          * @param {event} e The raw browser event
47559          */
47560         "dragover" : true,
47561         /**
47562          * @event dragenter
47563          *  Fires when the dragged row(s) first cross another DD target while being dragged
47564          * @param {Grid} this
47565          * @param {Roo.GridDD} dd The drag drop object
47566          * @param {String} targetId The target drag drop object
47567          * @param {event} e The raw browser event
47568          */
47569         "dragenter" : true,
47570         /**
47571          * @event dragout
47572          * Fires when the dragged row(s) leave another DD target while being dragged
47573          * @param {Grid} this
47574          * @param {Roo.GridDD} dd The drag drop object
47575          * @param {String} targetId The target drag drop object
47576          * @param {event} e The raw browser event
47577          */
47578         "dragout" : true,
47579         /**
47580          * @event rowclass
47581          * Fires when a row is rendered, so you can change add a style to it.
47582          * @param {GridView} gridview   The grid view
47583          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47584          */
47585         'rowclass' : true,
47586
47587         /**
47588          * @event render
47589          * Fires when the grid is rendered
47590          * @param {Grid} grid
47591          */
47592         'render' : true
47593     });
47594
47595     Roo.grid.Grid.superclass.constructor.call(this);
47596 };
47597 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47598     
47599     /**
47600      * @cfg {String} ddGroup - drag drop group.
47601      */
47602
47603     /**
47604      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47605      */
47606     minColumnWidth : 25,
47607
47608     /**
47609      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47610      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47611      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47612      */
47613     autoSizeColumns : false,
47614
47615     /**
47616      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47617      */
47618     autoSizeHeaders : true,
47619
47620     /**
47621      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47622      */
47623     monitorWindowResize : true,
47624
47625     /**
47626      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47627      * rows measured to get a columns size. Default is 0 (all rows).
47628      */
47629     maxRowsToMeasure : 0,
47630
47631     /**
47632      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47633      */
47634     trackMouseOver : true,
47635
47636     /**
47637     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47638     */
47639     
47640     /**
47641     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47642     */
47643     enableDragDrop : false,
47644     
47645     /**
47646     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47647     */
47648     enableColumnMove : true,
47649     
47650     /**
47651     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47652     */
47653     enableColumnHide : true,
47654     
47655     /**
47656     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47657     */
47658     enableRowHeightSync : false,
47659     
47660     /**
47661     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47662     */
47663     stripeRows : true,
47664     
47665     /**
47666     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47667     */
47668     autoHeight : false,
47669
47670     /**
47671      * @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.
47672      */
47673     autoExpandColumn : false,
47674
47675     /**
47676     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47677     * Default is 50.
47678     */
47679     autoExpandMin : 50,
47680
47681     /**
47682     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47683     */
47684     autoExpandMax : 1000,
47685
47686     /**
47687     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47688     */
47689     view : null,
47690
47691     /**
47692     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47693     */
47694     loadMask : false,
47695     /**
47696     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47697     */
47698     dropTarget: false,
47699     
47700    
47701     
47702     // private
47703     rendered : false,
47704
47705     /**
47706     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47707     * of a fixed width. Default is false.
47708     */
47709     /**
47710     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47711     */
47712     /**
47713      * Called once after all setup has been completed and the grid is ready to be rendered.
47714      * @return {Roo.grid.Grid} this
47715      */
47716     render : function()
47717     {
47718         var c = this.container;
47719         // try to detect autoHeight/width mode
47720         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47721             this.autoHeight = true;
47722         }
47723         var view = this.getView();
47724         view.init(this);
47725
47726         c.on("click", this.onClick, this);
47727         c.on("dblclick", this.onDblClick, this);
47728         c.on("contextmenu", this.onContextMenu, this);
47729         c.on("keydown", this.onKeyDown, this);
47730
47731         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47732
47733         this.getSelectionModel().init(this);
47734
47735         view.render();
47736
47737         if(this.loadMask){
47738             this.loadMask = new Roo.LoadMask(this.container,
47739                     Roo.apply({store:this.dataSource}, this.loadMask));
47740         }
47741         
47742         
47743         if (this.toolbar && this.toolbar.xtype) {
47744             this.toolbar.container = this.getView().getHeaderPanel(true);
47745             this.toolbar = new Roo.Toolbar(this.toolbar);
47746         }
47747         if (this.footer && this.footer.xtype) {
47748             this.footer.dataSource = this.getDataSource();
47749             this.footer.container = this.getView().getFooterPanel(true);
47750             this.footer = Roo.factory(this.footer, Roo);
47751         }
47752         if (this.dropTarget && this.dropTarget.xtype) {
47753             delete this.dropTarget.xtype;
47754             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47755         }
47756         
47757         
47758         this.rendered = true;
47759         this.fireEvent('render', this);
47760         return this;
47761     },
47762
47763         /**
47764          * Reconfigures the grid to use a different Store and Column Model.
47765          * The View will be bound to the new objects and refreshed.
47766          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47767          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47768          */
47769     reconfigure : function(dataSource, colModel){
47770         if(this.loadMask){
47771             this.loadMask.destroy();
47772             this.loadMask = new Roo.LoadMask(this.container,
47773                     Roo.apply({store:dataSource}, this.loadMask));
47774         }
47775         this.view.bind(dataSource, colModel);
47776         this.dataSource = dataSource;
47777         this.colModel = colModel;
47778         this.view.refresh(true);
47779     },
47780
47781     // private
47782     onKeyDown : function(e){
47783         this.fireEvent("keydown", e);
47784     },
47785
47786     /**
47787      * Destroy this grid.
47788      * @param {Boolean} removeEl True to remove the element
47789      */
47790     destroy : function(removeEl, keepListeners){
47791         if(this.loadMask){
47792             this.loadMask.destroy();
47793         }
47794         var c = this.container;
47795         c.removeAllListeners();
47796         this.view.destroy();
47797         this.colModel.purgeListeners();
47798         if(!keepListeners){
47799             this.purgeListeners();
47800         }
47801         c.update("");
47802         if(removeEl === true){
47803             c.remove();
47804         }
47805     },
47806
47807     // private
47808     processEvent : function(name, e){
47809         this.fireEvent(name, e);
47810         var t = e.getTarget();
47811         var v = this.view;
47812         var header = v.findHeaderIndex(t);
47813         if(header !== false){
47814             this.fireEvent("header" + name, this, header, e);
47815         }else{
47816             var row = v.findRowIndex(t);
47817             var cell = v.findCellIndex(t);
47818             if(row !== false){
47819                 this.fireEvent("row" + name, this, row, e);
47820                 if(cell !== false){
47821                     this.fireEvent("cell" + name, this, row, cell, e);
47822                 }
47823             }
47824         }
47825     },
47826
47827     // private
47828     onClick : function(e){
47829         this.processEvent("click", e);
47830     },
47831
47832     // private
47833     onContextMenu : function(e, t){
47834         this.processEvent("contextmenu", e);
47835     },
47836
47837     // private
47838     onDblClick : function(e){
47839         this.processEvent("dblclick", e);
47840     },
47841
47842     // private
47843     walkCells : function(row, col, step, fn, scope){
47844         var cm = this.colModel, clen = cm.getColumnCount();
47845         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47846         if(step < 0){
47847             if(col < 0){
47848                 row--;
47849                 first = false;
47850             }
47851             while(row >= 0){
47852                 if(!first){
47853                     col = clen-1;
47854                 }
47855                 first = false;
47856                 while(col >= 0){
47857                     if(fn.call(scope || this, row, col, cm) === true){
47858                         return [row, col];
47859                     }
47860                     col--;
47861                 }
47862                 row--;
47863             }
47864         } else {
47865             if(col >= clen){
47866                 row++;
47867                 first = false;
47868             }
47869             while(row < rlen){
47870                 if(!first){
47871                     col = 0;
47872                 }
47873                 first = false;
47874                 while(col < clen){
47875                     if(fn.call(scope || this, row, col, cm) === true){
47876                         return [row, col];
47877                     }
47878                     col++;
47879                 }
47880                 row++;
47881             }
47882         }
47883         return null;
47884     },
47885
47886     // private
47887     getSelections : function(){
47888         return this.selModel.getSelections();
47889     },
47890
47891     /**
47892      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47893      * but if manual update is required this method will initiate it.
47894      */
47895     autoSize : function(){
47896         if(this.rendered){
47897             this.view.layout();
47898             if(this.view.adjustForScroll){
47899                 this.view.adjustForScroll();
47900             }
47901         }
47902     },
47903
47904     /**
47905      * Returns the grid's underlying element.
47906      * @return {Element} The element
47907      */
47908     getGridEl : function(){
47909         return this.container;
47910     },
47911
47912     // private for compatibility, overridden by editor grid
47913     stopEditing : function(){},
47914
47915     /**
47916      * Returns the grid's SelectionModel.
47917      * @return {SelectionModel}
47918      */
47919     getSelectionModel : function(){
47920         if(!this.selModel){
47921             this.selModel = new Roo.grid.RowSelectionModel();
47922         }
47923         return this.selModel;
47924     },
47925
47926     /**
47927      * Returns the grid's DataSource.
47928      * @return {DataSource}
47929      */
47930     getDataSource : function(){
47931         return this.dataSource;
47932     },
47933
47934     /**
47935      * Returns the grid's ColumnModel.
47936      * @return {ColumnModel}
47937      */
47938     getColumnModel : function(){
47939         return this.colModel;
47940     },
47941
47942     /**
47943      * Returns the grid's GridView object.
47944      * @return {GridView}
47945      */
47946     getView : function(){
47947         if(!this.view){
47948             this.view = new Roo.grid.GridView(this.viewConfig);
47949         }
47950         return this.view;
47951     },
47952     /**
47953      * Called to get grid's drag proxy text, by default returns this.ddText.
47954      * @return {String}
47955      */
47956     getDragDropText : function(){
47957         var count = this.selModel.getCount();
47958         return String.format(this.ddText, count, count == 1 ? '' : 's');
47959     }
47960 });
47961 /**
47962  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47963  * %0 is replaced with the number of selected rows.
47964  * @type String
47965  */
47966 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47967  * Based on:
47968  * Ext JS Library 1.1.1
47969  * Copyright(c) 2006-2007, Ext JS, LLC.
47970  *
47971  * Originally Released Under LGPL - original licence link has changed is not relivant.
47972  *
47973  * Fork - LGPL
47974  * <script type="text/javascript">
47975  */
47976  
47977 Roo.grid.AbstractGridView = function(){
47978         this.grid = null;
47979         
47980         this.events = {
47981             "beforerowremoved" : true,
47982             "beforerowsinserted" : true,
47983             "beforerefresh" : true,
47984             "rowremoved" : true,
47985             "rowsinserted" : true,
47986             "rowupdated" : true,
47987             "refresh" : true
47988         };
47989     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47990 };
47991
47992 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47993     rowClass : "x-grid-row",
47994     cellClass : "x-grid-cell",
47995     tdClass : "x-grid-td",
47996     hdClass : "x-grid-hd",
47997     splitClass : "x-grid-hd-split",
47998     
47999         init: function(grid){
48000         this.grid = grid;
48001                 var cid = this.grid.getGridEl().id;
48002         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48003         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48004         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48005         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48006         },
48007         
48008         getColumnRenderers : function(){
48009         var renderers = [];
48010         var cm = this.grid.colModel;
48011         var colCount = cm.getColumnCount();
48012         for(var i = 0; i < colCount; i++){
48013             renderers[i] = cm.getRenderer(i);
48014         }
48015         return renderers;
48016     },
48017     
48018     getColumnIds : function(){
48019         var ids = [];
48020         var cm = this.grid.colModel;
48021         var colCount = cm.getColumnCount();
48022         for(var i = 0; i < colCount; i++){
48023             ids[i] = cm.getColumnId(i);
48024         }
48025         return ids;
48026     },
48027     
48028     getDataIndexes : function(){
48029         if(!this.indexMap){
48030             this.indexMap = this.buildIndexMap();
48031         }
48032         return this.indexMap.colToData;
48033     },
48034     
48035     getColumnIndexByDataIndex : function(dataIndex){
48036         if(!this.indexMap){
48037             this.indexMap = this.buildIndexMap();
48038         }
48039         return this.indexMap.dataToCol[dataIndex];
48040     },
48041     
48042     /**
48043      * Set a css style for a column dynamically. 
48044      * @param {Number} colIndex The index of the column
48045      * @param {String} name The css property name
48046      * @param {String} value The css value
48047      */
48048     setCSSStyle : function(colIndex, name, value){
48049         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48050         Roo.util.CSS.updateRule(selector, name, value);
48051     },
48052     
48053     generateRules : function(cm){
48054         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48055         Roo.util.CSS.removeStyleSheet(rulesId);
48056         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48057             var cid = cm.getColumnId(i);
48058             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48059                          this.tdSelector, cid, " {\n}\n",
48060                          this.hdSelector, cid, " {\n}\n",
48061                          this.splitSelector, cid, " {\n}\n");
48062         }
48063         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48064     }
48065 });/*
48066  * Based on:
48067  * Ext JS Library 1.1.1
48068  * Copyright(c) 2006-2007, Ext JS, LLC.
48069  *
48070  * Originally Released Under LGPL - original licence link has changed is not relivant.
48071  *
48072  * Fork - LGPL
48073  * <script type="text/javascript">
48074  */
48075
48076 // private
48077 // This is a support class used internally by the Grid components
48078 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48079     this.grid = grid;
48080     this.view = grid.getView();
48081     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48082     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48083     if(hd2){
48084         this.setHandleElId(Roo.id(hd));
48085         this.setOuterHandleElId(Roo.id(hd2));
48086     }
48087     this.scroll = false;
48088 };
48089 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48090     maxDragWidth: 120,
48091     getDragData : function(e){
48092         var t = Roo.lib.Event.getTarget(e);
48093         var h = this.view.findHeaderCell(t);
48094         if(h){
48095             return {ddel: h.firstChild, header:h};
48096         }
48097         return false;
48098     },
48099
48100     onInitDrag : function(e){
48101         this.view.headersDisabled = true;
48102         var clone = this.dragData.ddel.cloneNode(true);
48103         clone.id = Roo.id();
48104         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48105         this.proxy.update(clone);
48106         return true;
48107     },
48108
48109     afterValidDrop : function(){
48110         var v = this.view;
48111         setTimeout(function(){
48112             v.headersDisabled = false;
48113         }, 50);
48114     },
48115
48116     afterInvalidDrop : function(){
48117         var v = this.view;
48118         setTimeout(function(){
48119             v.headersDisabled = false;
48120         }, 50);
48121     }
48122 });
48123 /*
48124  * Based on:
48125  * Ext JS Library 1.1.1
48126  * Copyright(c) 2006-2007, Ext JS, LLC.
48127  *
48128  * Originally Released Under LGPL - original licence link has changed is not relivant.
48129  *
48130  * Fork - LGPL
48131  * <script type="text/javascript">
48132  */
48133 // private
48134 // This is a support class used internally by the Grid components
48135 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48136     this.grid = grid;
48137     this.view = grid.getView();
48138     // split the proxies so they don't interfere with mouse events
48139     this.proxyTop = Roo.DomHelper.append(document.body, {
48140         cls:"col-move-top", html:"&#160;"
48141     }, true);
48142     this.proxyBottom = Roo.DomHelper.append(document.body, {
48143         cls:"col-move-bottom", html:"&#160;"
48144     }, true);
48145     this.proxyTop.hide = this.proxyBottom.hide = function(){
48146         this.setLeftTop(-100,-100);
48147         this.setStyle("visibility", "hidden");
48148     };
48149     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48150     // temporarily disabled
48151     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48152     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48153 };
48154 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48155     proxyOffsets : [-4, -9],
48156     fly: Roo.Element.fly,
48157
48158     getTargetFromEvent : function(e){
48159         var t = Roo.lib.Event.getTarget(e);
48160         var cindex = this.view.findCellIndex(t);
48161         if(cindex !== false){
48162             return this.view.getHeaderCell(cindex);
48163         }
48164         return null;
48165     },
48166
48167     nextVisible : function(h){
48168         var v = this.view, cm = this.grid.colModel;
48169         h = h.nextSibling;
48170         while(h){
48171             if(!cm.isHidden(v.getCellIndex(h))){
48172                 return h;
48173             }
48174             h = h.nextSibling;
48175         }
48176         return null;
48177     },
48178
48179     prevVisible : function(h){
48180         var v = this.view, cm = this.grid.colModel;
48181         h = h.prevSibling;
48182         while(h){
48183             if(!cm.isHidden(v.getCellIndex(h))){
48184                 return h;
48185             }
48186             h = h.prevSibling;
48187         }
48188         return null;
48189     },
48190
48191     positionIndicator : function(h, n, e){
48192         var x = Roo.lib.Event.getPageX(e);
48193         var r = Roo.lib.Dom.getRegion(n.firstChild);
48194         var px, pt, py = r.top + this.proxyOffsets[1];
48195         if((r.right - x) <= (r.right-r.left)/2){
48196             px = r.right+this.view.borderWidth;
48197             pt = "after";
48198         }else{
48199             px = r.left;
48200             pt = "before";
48201         }
48202         var oldIndex = this.view.getCellIndex(h);
48203         var newIndex = this.view.getCellIndex(n);
48204
48205         if(this.grid.colModel.isFixed(newIndex)){
48206             return false;
48207         }
48208
48209         var locked = this.grid.colModel.isLocked(newIndex);
48210
48211         if(pt == "after"){
48212             newIndex++;
48213         }
48214         if(oldIndex < newIndex){
48215             newIndex--;
48216         }
48217         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48218             return false;
48219         }
48220         px +=  this.proxyOffsets[0];
48221         this.proxyTop.setLeftTop(px, py);
48222         this.proxyTop.show();
48223         if(!this.bottomOffset){
48224             this.bottomOffset = this.view.mainHd.getHeight();
48225         }
48226         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48227         this.proxyBottom.show();
48228         return pt;
48229     },
48230
48231     onNodeEnter : function(n, dd, e, data){
48232         if(data.header != n){
48233             this.positionIndicator(data.header, n, e);
48234         }
48235     },
48236
48237     onNodeOver : function(n, dd, e, data){
48238         var result = false;
48239         if(data.header != n){
48240             result = this.positionIndicator(data.header, n, e);
48241         }
48242         if(!result){
48243             this.proxyTop.hide();
48244             this.proxyBottom.hide();
48245         }
48246         return result ? this.dropAllowed : this.dropNotAllowed;
48247     },
48248
48249     onNodeOut : function(n, dd, e, data){
48250         this.proxyTop.hide();
48251         this.proxyBottom.hide();
48252     },
48253
48254     onNodeDrop : function(n, dd, e, data){
48255         var h = data.header;
48256         if(h != n){
48257             var cm = this.grid.colModel;
48258             var x = Roo.lib.Event.getPageX(e);
48259             var r = Roo.lib.Dom.getRegion(n.firstChild);
48260             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48261             var oldIndex = this.view.getCellIndex(h);
48262             var newIndex = this.view.getCellIndex(n);
48263             var locked = cm.isLocked(newIndex);
48264             if(pt == "after"){
48265                 newIndex++;
48266             }
48267             if(oldIndex < newIndex){
48268                 newIndex--;
48269             }
48270             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48271                 return false;
48272             }
48273             cm.setLocked(oldIndex, locked, true);
48274             cm.moveColumn(oldIndex, newIndex);
48275             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48276             return true;
48277         }
48278         return false;
48279     }
48280 });
48281 /*
48282  * Based on:
48283  * Ext JS Library 1.1.1
48284  * Copyright(c) 2006-2007, Ext JS, LLC.
48285  *
48286  * Originally Released Under LGPL - original licence link has changed is not relivant.
48287  *
48288  * Fork - LGPL
48289  * <script type="text/javascript">
48290  */
48291   
48292 /**
48293  * @class Roo.grid.GridView
48294  * @extends Roo.util.Observable
48295  *
48296  * @constructor
48297  * @param {Object} config
48298  */
48299 Roo.grid.GridView = function(config){
48300     Roo.grid.GridView.superclass.constructor.call(this);
48301     this.el = null;
48302
48303     Roo.apply(this, config);
48304 };
48305
48306 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48307
48308     /**
48309      * Override this function to apply custom css classes to rows during rendering
48310      * @param {Record} record The record
48311      * @param {Number} index
48312      * @method getRowClass
48313      */
48314     rowClass : "x-grid-row",
48315
48316     cellClass : "x-grid-col",
48317
48318     tdClass : "x-grid-td",
48319
48320     hdClass : "x-grid-hd",
48321
48322     splitClass : "x-grid-split",
48323
48324     sortClasses : ["sort-asc", "sort-desc"],
48325
48326     enableMoveAnim : false,
48327
48328     hlColor: "C3DAF9",
48329
48330     dh : Roo.DomHelper,
48331
48332     fly : Roo.Element.fly,
48333
48334     css : Roo.util.CSS,
48335
48336     borderWidth: 1,
48337
48338     splitOffset: 3,
48339
48340     scrollIncrement : 22,
48341
48342     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48343
48344     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48345
48346     bind : function(ds, cm){
48347         if(this.ds){
48348             this.ds.un("load", this.onLoad, this);
48349             this.ds.un("datachanged", this.onDataChange, this);
48350             this.ds.un("add", this.onAdd, this);
48351             this.ds.un("remove", this.onRemove, this);
48352             this.ds.un("update", this.onUpdate, this);
48353             this.ds.un("clear", this.onClear, this);
48354         }
48355         if(ds){
48356             ds.on("load", this.onLoad, this);
48357             ds.on("datachanged", this.onDataChange, this);
48358             ds.on("add", this.onAdd, this);
48359             ds.on("remove", this.onRemove, this);
48360             ds.on("update", this.onUpdate, this);
48361             ds.on("clear", this.onClear, this);
48362         }
48363         this.ds = ds;
48364
48365         if(this.cm){
48366             this.cm.un("widthchange", this.onColWidthChange, this);
48367             this.cm.un("headerchange", this.onHeaderChange, this);
48368             this.cm.un("hiddenchange", this.onHiddenChange, this);
48369             this.cm.un("columnmoved", this.onColumnMove, this);
48370             this.cm.un("columnlockchange", this.onColumnLock, this);
48371         }
48372         if(cm){
48373             this.generateRules(cm);
48374             cm.on("widthchange", this.onColWidthChange, this);
48375             cm.on("headerchange", this.onHeaderChange, this);
48376             cm.on("hiddenchange", this.onHiddenChange, this);
48377             cm.on("columnmoved", this.onColumnMove, this);
48378             cm.on("columnlockchange", this.onColumnLock, this);
48379         }
48380         this.cm = cm;
48381     },
48382
48383     init: function(grid){
48384         Roo.grid.GridView.superclass.init.call(this, grid);
48385
48386         this.bind(grid.dataSource, grid.colModel);
48387
48388         grid.on("headerclick", this.handleHeaderClick, this);
48389
48390         if(grid.trackMouseOver){
48391             grid.on("mouseover", this.onRowOver, this);
48392             grid.on("mouseout", this.onRowOut, this);
48393         }
48394         grid.cancelTextSelection = function(){};
48395         this.gridId = grid.id;
48396
48397         var tpls = this.templates || {};
48398
48399         if(!tpls.master){
48400             tpls.master = new Roo.Template(
48401                '<div class="x-grid" hidefocus="true">',
48402                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48403                   '<div class="x-grid-topbar"></div>',
48404                   '<div class="x-grid-scroller"><div></div></div>',
48405                   '<div class="x-grid-locked">',
48406                       '<div class="x-grid-header">{lockedHeader}</div>',
48407                       '<div class="x-grid-body">{lockedBody}</div>',
48408                   "</div>",
48409                   '<div class="x-grid-viewport">',
48410                       '<div class="x-grid-header">{header}</div>',
48411                       '<div class="x-grid-body">{body}</div>',
48412                   "</div>",
48413                   '<div class="x-grid-bottombar"></div>',
48414                  
48415                   '<div class="x-grid-resize-proxy">&#160;</div>',
48416                "</div>"
48417             );
48418             tpls.master.disableformats = true;
48419         }
48420
48421         if(!tpls.header){
48422             tpls.header = new Roo.Template(
48423                '<table border="0" cellspacing="0" cellpadding="0">',
48424                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48425                "</table>{splits}"
48426             );
48427             tpls.header.disableformats = true;
48428         }
48429         tpls.header.compile();
48430
48431         if(!tpls.hcell){
48432             tpls.hcell = new Roo.Template(
48433                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48434                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48435                 "</div></td>"
48436              );
48437              tpls.hcell.disableFormats = true;
48438         }
48439         tpls.hcell.compile();
48440
48441         if(!tpls.hsplit){
48442             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48443             tpls.hsplit.disableFormats = true;
48444         }
48445         tpls.hsplit.compile();
48446
48447         if(!tpls.body){
48448             tpls.body = new Roo.Template(
48449                '<table border="0" cellspacing="0" cellpadding="0">',
48450                "<tbody>{rows}</tbody>",
48451                "</table>"
48452             );
48453             tpls.body.disableFormats = true;
48454         }
48455         tpls.body.compile();
48456
48457         if(!tpls.row){
48458             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48459             tpls.row.disableFormats = true;
48460         }
48461         tpls.row.compile();
48462
48463         if(!tpls.cell){
48464             tpls.cell = new Roo.Template(
48465                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48466                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48467                 "</td>"
48468             );
48469             tpls.cell.disableFormats = true;
48470         }
48471         tpls.cell.compile();
48472
48473         this.templates = tpls;
48474     },
48475
48476     // remap these for backwards compat
48477     onColWidthChange : function(){
48478         this.updateColumns.apply(this, arguments);
48479     },
48480     onHeaderChange : function(){
48481         this.updateHeaders.apply(this, arguments);
48482     }, 
48483     onHiddenChange : function(){
48484         this.handleHiddenChange.apply(this, arguments);
48485     },
48486     onColumnMove : function(){
48487         this.handleColumnMove.apply(this, arguments);
48488     },
48489     onColumnLock : function(){
48490         this.handleLockChange.apply(this, arguments);
48491     },
48492
48493     onDataChange : function(){
48494         this.refresh();
48495         this.updateHeaderSortState();
48496     },
48497
48498     onClear : function(){
48499         this.refresh();
48500     },
48501
48502     onUpdate : function(ds, record){
48503         this.refreshRow(record);
48504     },
48505
48506     refreshRow : function(record){
48507         var ds = this.ds, index;
48508         if(typeof record == 'number'){
48509             index = record;
48510             record = ds.getAt(index);
48511         }else{
48512             index = ds.indexOf(record);
48513         }
48514         this.insertRows(ds, index, index, true);
48515         this.onRemove(ds, record, index+1, true);
48516         this.syncRowHeights(index, index);
48517         this.layout();
48518         this.fireEvent("rowupdated", this, index, record);
48519     },
48520
48521     onAdd : function(ds, records, index){
48522         this.insertRows(ds, index, index + (records.length-1));
48523     },
48524
48525     onRemove : function(ds, record, index, isUpdate){
48526         if(isUpdate !== true){
48527             this.fireEvent("beforerowremoved", this, index, record);
48528         }
48529         var bt = this.getBodyTable(), lt = this.getLockedTable();
48530         if(bt.rows[index]){
48531             bt.firstChild.removeChild(bt.rows[index]);
48532         }
48533         if(lt.rows[index]){
48534             lt.firstChild.removeChild(lt.rows[index]);
48535         }
48536         if(isUpdate !== true){
48537             this.stripeRows(index);
48538             this.syncRowHeights(index, index);
48539             this.layout();
48540             this.fireEvent("rowremoved", this, index, record);
48541         }
48542     },
48543
48544     onLoad : function(){
48545         this.scrollToTop();
48546     },
48547
48548     /**
48549      * Scrolls the grid to the top
48550      */
48551     scrollToTop : function(){
48552         if(this.scroller){
48553             this.scroller.dom.scrollTop = 0;
48554             this.syncScroll();
48555         }
48556     },
48557
48558     /**
48559      * Gets a panel in the header of the grid that can be used for toolbars etc.
48560      * After modifying the contents of this panel a call to grid.autoSize() may be
48561      * required to register any changes in size.
48562      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48563      * @return Roo.Element
48564      */
48565     getHeaderPanel : function(doShow){
48566         if(doShow){
48567             this.headerPanel.show();
48568         }
48569         return this.headerPanel;
48570     },
48571
48572     /**
48573      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48574      * After modifying the contents of this panel a call to grid.autoSize() may be
48575      * required to register any changes in size.
48576      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48577      * @return Roo.Element
48578      */
48579     getFooterPanel : function(doShow){
48580         if(doShow){
48581             this.footerPanel.show();
48582         }
48583         return this.footerPanel;
48584     },
48585
48586     initElements : function(){
48587         var E = Roo.Element;
48588         var el = this.grid.getGridEl().dom.firstChild;
48589         var cs = el.childNodes;
48590
48591         this.el = new E(el);
48592         
48593          this.focusEl = new E(el.firstChild);
48594         this.focusEl.swallowEvent("click", true);
48595         
48596         this.headerPanel = new E(cs[1]);
48597         this.headerPanel.enableDisplayMode("block");
48598
48599         this.scroller = new E(cs[2]);
48600         this.scrollSizer = new E(this.scroller.dom.firstChild);
48601
48602         this.lockedWrap = new E(cs[3]);
48603         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48604         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48605
48606         this.mainWrap = new E(cs[4]);
48607         this.mainHd = new E(this.mainWrap.dom.firstChild);
48608         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48609
48610         this.footerPanel = new E(cs[5]);
48611         this.footerPanel.enableDisplayMode("block");
48612
48613         this.resizeProxy = new E(cs[6]);
48614
48615         this.headerSelector = String.format(
48616            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48617            this.lockedHd.id, this.mainHd.id
48618         );
48619
48620         this.splitterSelector = String.format(
48621            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48622            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48623         );
48624     },
48625     idToCssName : function(s)
48626     {
48627         return s.replace(/[^a-z0-9]+/ig, '-');
48628     },
48629
48630     getHeaderCell : function(index){
48631         return Roo.DomQuery.select(this.headerSelector)[index];
48632     },
48633
48634     getHeaderCellMeasure : function(index){
48635         return this.getHeaderCell(index).firstChild;
48636     },
48637
48638     getHeaderCellText : function(index){
48639         return this.getHeaderCell(index).firstChild.firstChild;
48640     },
48641
48642     getLockedTable : function(){
48643         return this.lockedBody.dom.firstChild;
48644     },
48645
48646     getBodyTable : function(){
48647         return this.mainBody.dom.firstChild;
48648     },
48649
48650     getLockedRow : function(index){
48651         return this.getLockedTable().rows[index];
48652     },
48653
48654     getRow : function(index){
48655         return this.getBodyTable().rows[index];
48656     },
48657
48658     getRowComposite : function(index){
48659         if(!this.rowEl){
48660             this.rowEl = new Roo.CompositeElementLite();
48661         }
48662         var els = [], lrow, mrow;
48663         if(lrow = this.getLockedRow(index)){
48664             els.push(lrow);
48665         }
48666         if(mrow = this.getRow(index)){
48667             els.push(mrow);
48668         }
48669         this.rowEl.elements = els;
48670         return this.rowEl;
48671     },
48672     /**
48673      * Gets the 'td' of the cell
48674      * 
48675      * @param {Integer} rowIndex row to select
48676      * @param {Integer} colIndex column to select
48677      * 
48678      * @return {Object} 
48679      */
48680     getCell : function(rowIndex, colIndex){
48681         var locked = this.cm.getLockedCount();
48682         var source;
48683         if(colIndex < locked){
48684             source = this.lockedBody.dom.firstChild;
48685         }else{
48686             source = this.mainBody.dom.firstChild;
48687             colIndex -= locked;
48688         }
48689         return source.rows[rowIndex].childNodes[colIndex];
48690     },
48691
48692     getCellText : function(rowIndex, colIndex){
48693         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48694     },
48695
48696     getCellBox : function(cell){
48697         var b = this.fly(cell).getBox();
48698         if(Roo.isOpera){ // opera fails to report the Y
48699             b.y = cell.offsetTop + this.mainBody.getY();
48700         }
48701         return b;
48702     },
48703
48704     getCellIndex : function(cell){
48705         var id = String(cell.className).match(this.cellRE);
48706         if(id){
48707             return parseInt(id[1], 10);
48708         }
48709         return 0;
48710     },
48711
48712     findHeaderIndex : function(n){
48713         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48714         return r ? this.getCellIndex(r) : false;
48715     },
48716
48717     findHeaderCell : function(n){
48718         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48719         return r ? r : false;
48720     },
48721
48722     findRowIndex : function(n){
48723         if(!n){
48724             return false;
48725         }
48726         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48727         return r ? r.rowIndex : false;
48728     },
48729
48730     findCellIndex : function(node){
48731         var stop = this.el.dom;
48732         while(node && node != stop){
48733             if(this.findRE.test(node.className)){
48734                 return this.getCellIndex(node);
48735             }
48736             node = node.parentNode;
48737         }
48738         return false;
48739     },
48740
48741     getColumnId : function(index){
48742         return this.cm.getColumnId(index);
48743     },
48744
48745     getSplitters : function()
48746     {
48747         if(this.splitterSelector){
48748            return Roo.DomQuery.select(this.splitterSelector);
48749         }else{
48750             return null;
48751       }
48752     },
48753
48754     getSplitter : function(index){
48755         return this.getSplitters()[index];
48756     },
48757
48758     onRowOver : function(e, t){
48759         var row;
48760         if((row = this.findRowIndex(t)) !== false){
48761             this.getRowComposite(row).addClass("x-grid-row-over");
48762         }
48763     },
48764
48765     onRowOut : function(e, t){
48766         var row;
48767         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48768             this.getRowComposite(row).removeClass("x-grid-row-over");
48769         }
48770     },
48771
48772     renderHeaders : function(){
48773         var cm = this.cm;
48774         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48775         var cb = [], lb = [], sb = [], lsb = [], p = {};
48776         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48777             p.cellId = "x-grid-hd-0-" + i;
48778             p.splitId = "x-grid-csplit-0-" + i;
48779             p.id = cm.getColumnId(i);
48780             p.title = cm.getColumnTooltip(i) || "";
48781             p.value = cm.getColumnHeader(i) || "";
48782             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48783             if(!cm.isLocked(i)){
48784                 cb[cb.length] = ct.apply(p);
48785                 sb[sb.length] = st.apply(p);
48786             }else{
48787                 lb[lb.length] = ct.apply(p);
48788                 lsb[lsb.length] = st.apply(p);
48789             }
48790         }
48791         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48792                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48793     },
48794
48795     updateHeaders : function(){
48796         var html = this.renderHeaders();
48797         this.lockedHd.update(html[0]);
48798         this.mainHd.update(html[1]);
48799     },
48800
48801     /**
48802      * Focuses the specified row.
48803      * @param {Number} row The row index
48804      */
48805     focusRow : function(row)
48806     {
48807         //Roo.log('GridView.focusRow');
48808         var x = this.scroller.dom.scrollLeft;
48809         this.focusCell(row, 0, false);
48810         this.scroller.dom.scrollLeft = x;
48811     },
48812
48813     /**
48814      * Focuses the specified cell.
48815      * @param {Number} row The row index
48816      * @param {Number} col The column index
48817      * @param {Boolean} hscroll false to disable horizontal scrolling
48818      */
48819     focusCell : function(row, col, hscroll)
48820     {
48821         //Roo.log('GridView.focusCell');
48822         var el = this.ensureVisible(row, col, hscroll);
48823         this.focusEl.alignTo(el, "tl-tl");
48824         if(Roo.isGecko){
48825             this.focusEl.focus();
48826         }else{
48827             this.focusEl.focus.defer(1, this.focusEl);
48828         }
48829     },
48830
48831     /**
48832      * Scrolls the specified cell into view
48833      * @param {Number} row The row index
48834      * @param {Number} col The column index
48835      * @param {Boolean} hscroll false to disable horizontal scrolling
48836      */
48837     ensureVisible : function(row, col, hscroll)
48838     {
48839         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48840         //return null; //disable for testing.
48841         if(typeof row != "number"){
48842             row = row.rowIndex;
48843         }
48844         if(row < 0 && row >= this.ds.getCount()){
48845             return  null;
48846         }
48847         col = (col !== undefined ? col : 0);
48848         var cm = this.grid.colModel;
48849         while(cm.isHidden(col)){
48850             col++;
48851         }
48852
48853         var el = this.getCell(row, col);
48854         if(!el){
48855             return null;
48856         }
48857         var c = this.scroller.dom;
48858
48859         var ctop = parseInt(el.offsetTop, 10);
48860         var cleft = parseInt(el.offsetLeft, 10);
48861         var cbot = ctop + el.offsetHeight;
48862         var cright = cleft + el.offsetWidth;
48863         
48864         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48865         var stop = parseInt(c.scrollTop, 10);
48866         var sleft = parseInt(c.scrollLeft, 10);
48867         var sbot = stop + ch;
48868         var sright = sleft + c.clientWidth;
48869         /*
48870         Roo.log('GridView.ensureVisible:' +
48871                 ' ctop:' + ctop +
48872                 ' c.clientHeight:' + c.clientHeight +
48873                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48874                 ' stop:' + stop +
48875                 ' cbot:' + cbot +
48876                 ' sbot:' + sbot +
48877                 ' ch:' + ch  
48878                 );
48879         */
48880         if(ctop < stop){
48881              c.scrollTop = ctop;
48882             //Roo.log("set scrolltop to ctop DISABLE?");
48883         }else if(cbot > sbot){
48884             //Roo.log("set scrolltop to cbot-ch");
48885             c.scrollTop = cbot-ch;
48886         }
48887         
48888         if(hscroll !== false){
48889             if(cleft < sleft){
48890                 c.scrollLeft = cleft;
48891             }else if(cright > sright){
48892                 c.scrollLeft = cright-c.clientWidth;
48893             }
48894         }
48895          
48896         return el;
48897     },
48898
48899     updateColumns : function(){
48900         this.grid.stopEditing();
48901         var cm = this.grid.colModel, colIds = this.getColumnIds();
48902         //var totalWidth = cm.getTotalWidth();
48903         var pos = 0;
48904         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48905             //if(cm.isHidden(i)) continue;
48906             var w = cm.getColumnWidth(i);
48907             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48908             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48909         }
48910         this.updateSplitters();
48911     },
48912
48913     generateRules : function(cm){
48914         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48915         Roo.util.CSS.removeStyleSheet(rulesId);
48916         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48917             var cid = cm.getColumnId(i);
48918             var align = '';
48919             if(cm.config[i].align){
48920                 align = 'text-align:'+cm.config[i].align+';';
48921             }
48922             var hidden = '';
48923             if(cm.isHidden(i)){
48924                 hidden = 'display:none;';
48925             }
48926             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48927             ruleBuf.push(
48928                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48929                     this.hdSelector, cid, " {\n", align, width, "}\n",
48930                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48931                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48932         }
48933         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48934     },
48935
48936     updateSplitters : function(){
48937         var cm = this.cm, s = this.getSplitters();
48938         if(s){ // splitters not created yet
48939             var pos = 0, locked = true;
48940             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48941                 if(cm.isHidden(i)) continue;
48942                 var w = cm.getColumnWidth(i); // make sure it's a number
48943                 if(!cm.isLocked(i) && locked){
48944                     pos = 0;
48945                     locked = false;
48946                 }
48947                 pos += w;
48948                 s[i].style.left = (pos-this.splitOffset) + "px";
48949             }
48950         }
48951     },
48952
48953     handleHiddenChange : function(colModel, colIndex, hidden){
48954         if(hidden){
48955             this.hideColumn(colIndex);
48956         }else{
48957             this.unhideColumn(colIndex);
48958         }
48959     },
48960
48961     hideColumn : function(colIndex){
48962         var cid = this.getColumnId(colIndex);
48963         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48964         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48965         if(Roo.isSafari){
48966             this.updateHeaders();
48967         }
48968         this.updateSplitters();
48969         this.layout();
48970     },
48971
48972     unhideColumn : function(colIndex){
48973         var cid = this.getColumnId(colIndex);
48974         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48975         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48976
48977         if(Roo.isSafari){
48978             this.updateHeaders();
48979         }
48980         this.updateSplitters();
48981         this.layout();
48982     },
48983
48984     insertRows : function(dm, firstRow, lastRow, isUpdate){
48985         if(firstRow == 0 && lastRow == dm.getCount()-1){
48986             this.refresh();
48987         }else{
48988             if(!isUpdate){
48989                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48990             }
48991             var s = this.getScrollState();
48992             var markup = this.renderRows(firstRow, lastRow);
48993             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48994             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48995             this.restoreScroll(s);
48996             if(!isUpdate){
48997                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48998                 this.syncRowHeights(firstRow, lastRow);
48999                 this.stripeRows(firstRow);
49000                 this.layout();
49001             }
49002         }
49003     },
49004
49005     bufferRows : function(markup, target, index){
49006         var before = null, trows = target.rows, tbody = target.tBodies[0];
49007         if(index < trows.length){
49008             before = trows[index];
49009         }
49010         var b = document.createElement("div");
49011         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49012         var rows = b.firstChild.rows;
49013         for(var i = 0, len = rows.length; i < len; i++){
49014             if(before){
49015                 tbody.insertBefore(rows[0], before);
49016             }else{
49017                 tbody.appendChild(rows[0]);
49018             }
49019         }
49020         b.innerHTML = "";
49021         b = null;
49022     },
49023
49024     deleteRows : function(dm, firstRow, lastRow){
49025         if(dm.getRowCount()<1){
49026             this.fireEvent("beforerefresh", this);
49027             this.mainBody.update("");
49028             this.lockedBody.update("");
49029             this.fireEvent("refresh", this);
49030         }else{
49031             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49032             var bt = this.getBodyTable();
49033             var tbody = bt.firstChild;
49034             var rows = bt.rows;
49035             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49036                 tbody.removeChild(rows[firstRow]);
49037             }
49038             this.stripeRows(firstRow);
49039             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49040         }
49041     },
49042
49043     updateRows : function(dataSource, firstRow, lastRow){
49044         var s = this.getScrollState();
49045         this.refresh();
49046         this.restoreScroll(s);
49047     },
49048
49049     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49050         if(!noRefresh){
49051            this.refresh();
49052         }
49053         this.updateHeaderSortState();
49054     },
49055
49056     getScrollState : function(){
49057         
49058         var sb = this.scroller.dom;
49059         return {left: sb.scrollLeft, top: sb.scrollTop};
49060     },
49061
49062     stripeRows : function(startRow){
49063         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49064             return;
49065         }
49066         startRow = startRow || 0;
49067         var rows = this.getBodyTable().rows;
49068         var lrows = this.getLockedTable().rows;
49069         var cls = ' x-grid-row-alt ';
49070         for(var i = startRow, len = rows.length; i < len; i++){
49071             var row = rows[i], lrow = lrows[i];
49072             var isAlt = ((i+1) % 2 == 0);
49073             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49074             if(isAlt == hasAlt){
49075                 continue;
49076             }
49077             if(isAlt){
49078                 row.className += " x-grid-row-alt";
49079             }else{
49080                 row.className = row.className.replace("x-grid-row-alt", "");
49081             }
49082             if(lrow){
49083                 lrow.className = row.className;
49084             }
49085         }
49086     },
49087
49088     restoreScroll : function(state){
49089         //Roo.log('GridView.restoreScroll');
49090         var sb = this.scroller.dom;
49091         sb.scrollLeft = state.left;
49092         sb.scrollTop = state.top;
49093         this.syncScroll();
49094     },
49095
49096     syncScroll : function(){
49097         //Roo.log('GridView.syncScroll');
49098         var sb = this.scroller.dom;
49099         var sh = this.mainHd.dom;
49100         var bs = this.mainBody.dom;
49101         var lv = this.lockedBody.dom;
49102         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49103         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49104     },
49105
49106     handleScroll : function(e){
49107         this.syncScroll();
49108         var sb = this.scroller.dom;
49109         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49110         e.stopEvent();
49111     },
49112
49113     handleWheel : function(e){
49114         var d = e.getWheelDelta();
49115         this.scroller.dom.scrollTop -= d*22;
49116         // set this here to prevent jumpy scrolling on large tables
49117         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49118         e.stopEvent();
49119     },
49120
49121     renderRows : function(startRow, endRow){
49122         // pull in all the crap needed to render rows
49123         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49124         var colCount = cm.getColumnCount();
49125
49126         if(ds.getCount() < 1){
49127             return ["", ""];
49128         }
49129
49130         // build a map for all the columns
49131         var cs = [];
49132         for(var i = 0; i < colCount; i++){
49133             var name = cm.getDataIndex(i);
49134             cs[i] = {
49135                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49136                 renderer : cm.getRenderer(i),
49137                 id : cm.getColumnId(i),
49138                 locked : cm.isLocked(i)
49139             };
49140         }
49141
49142         startRow = startRow || 0;
49143         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49144
49145         // records to render
49146         var rs = ds.getRange(startRow, endRow);
49147
49148         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49149     },
49150
49151     // As much as I hate to duplicate code, this was branched because FireFox really hates
49152     // [].join("") on strings. The performance difference was substantial enough to
49153     // branch this function
49154     doRender : Roo.isGecko ?
49155             function(cs, rs, ds, startRow, colCount, stripe){
49156                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49157                 // buffers
49158                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49159                 
49160                 var hasListener = this.grid.hasListener('rowclass');
49161                 var rowcfg = {};
49162                 for(var j = 0, len = rs.length; j < len; j++){
49163                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49164                     for(var i = 0; i < colCount; i++){
49165                         c = cs[i];
49166                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49167                         p.id = c.id;
49168                         p.css = p.attr = "";
49169                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49170                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49171                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49172                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49173                         }
49174                         var markup = ct.apply(p);
49175                         if(!c.locked){
49176                             cb+= markup;
49177                         }else{
49178                             lcb+= markup;
49179                         }
49180                     }
49181                     var alt = [];
49182                     if(stripe && ((rowIndex+1) % 2 == 0)){
49183                         alt.push("x-grid-row-alt")
49184                     }
49185                     if(r.dirty){
49186                         alt.push(  " x-grid-dirty-row");
49187                     }
49188                     rp.cells = lcb;
49189                     if(this.getRowClass){
49190                         alt.push(this.getRowClass(r, rowIndex));
49191                     }
49192                     if (hasListener) {
49193                         rowcfg = {
49194                              
49195                             record: r,
49196                             rowIndex : rowIndex,
49197                             rowClass : ''
49198                         }
49199                         this.grid.fireEvent('rowclass', this, rowcfg);
49200                         alt.push(rowcfg.rowClass);
49201                     }
49202                     rp.alt = alt.join(" ");
49203                     lbuf+= rt.apply(rp);
49204                     rp.cells = cb;
49205                     buf+=  rt.apply(rp);
49206                 }
49207                 return [lbuf, buf];
49208             } :
49209             function(cs, rs, ds, startRow, colCount, stripe){
49210                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49211                 // buffers
49212                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49213                 var hasListener = this.grid.hasListener('rowclass');
49214                 var rowcfg = {};
49215                 for(var j = 0, len = rs.length; j < len; j++){
49216                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49217                     for(var i = 0; i < colCount; i++){
49218                         c = cs[i];
49219                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49220                         p.id = c.id;
49221                         p.css = p.attr = "";
49222                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49223                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49224                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49225                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49226                         }
49227                         var markup = ct.apply(p);
49228                         if(!c.locked){
49229                             cb[cb.length] = markup;
49230                         }else{
49231                             lcb[lcb.length] = markup;
49232                         }
49233                     }
49234                     var alt = [];
49235                     if(stripe && ((rowIndex+1) % 2 == 0)){
49236                         alt.push( "x-grid-row-alt");
49237                     }
49238                     if(r.dirty){
49239                         alt.push(" x-grid-dirty-row");
49240                     }
49241                     rp.cells = lcb;
49242                     if(this.getRowClass){
49243                         alt.push( this.getRowClass(r, rowIndex));
49244                     }
49245                     if (hasListener) {
49246                         rowcfg = {
49247                              
49248                             record: r,
49249                             rowIndex : rowIndex,
49250                             rowClass : ''
49251                         }
49252                         this.grid.fireEvent('rowclass', this, rowcfg);
49253                         alt.push(rowcfg.rowClass);
49254                     }
49255                     rp.alt = alt.join(" ");
49256                     rp.cells = lcb.join("");
49257                     lbuf[lbuf.length] = rt.apply(rp);
49258                     rp.cells = cb.join("");
49259                     buf[buf.length] =  rt.apply(rp);
49260                 }
49261                 return [lbuf.join(""), buf.join("")];
49262             },
49263
49264     renderBody : function(){
49265         var markup = this.renderRows();
49266         var bt = this.templates.body;
49267         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49268     },
49269
49270     /**
49271      * Refreshes the grid
49272      * @param {Boolean} headersToo
49273      */
49274     refresh : function(headersToo){
49275         this.fireEvent("beforerefresh", this);
49276         this.grid.stopEditing();
49277         var result = this.renderBody();
49278         this.lockedBody.update(result[0]);
49279         this.mainBody.update(result[1]);
49280         if(headersToo === true){
49281             this.updateHeaders();
49282             this.updateColumns();
49283             this.updateSplitters();
49284             this.updateHeaderSortState();
49285         }
49286         this.syncRowHeights();
49287         this.layout();
49288         this.fireEvent("refresh", this);
49289     },
49290
49291     handleColumnMove : function(cm, oldIndex, newIndex){
49292         this.indexMap = null;
49293         var s = this.getScrollState();
49294         this.refresh(true);
49295         this.restoreScroll(s);
49296         this.afterMove(newIndex);
49297     },
49298
49299     afterMove : function(colIndex){
49300         if(this.enableMoveAnim && Roo.enableFx){
49301             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49302         }
49303         // if multisort - fix sortOrder, and reload..
49304         if (this.grid.dataSource.multiSort) {
49305             // the we can call sort again..
49306             var dm = this.grid.dataSource;
49307             var cm = this.grid.colModel;
49308             var so = [];
49309             for(var i = 0; i < cm.config.length; i++ ) {
49310                 
49311                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49312                     continue; // dont' bother, it's not in sort list or being set.
49313                 }
49314                 
49315                 so.push(cm.config[i].dataIndex);
49316             };
49317             dm.sortOrder = so;
49318             dm.load(dm.lastOptions);
49319             
49320             
49321         }
49322         
49323     },
49324
49325     updateCell : function(dm, rowIndex, dataIndex){
49326         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49327         if(typeof colIndex == "undefined"){ // not present in grid
49328             return;
49329         }
49330         var cm = this.grid.colModel;
49331         var cell = this.getCell(rowIndex, colIndex);
49332         var cellText = this.getCellText(rowIndex, colIndex);
49333
49334         var p = {
49335             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49336             id : cm.getColumnId(colIndex),
49337             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49338         };
49339         var renderer = cm.getRenderer(colIndex);
49340         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49341         if(typeof val == "undefined" || val === "") val = "&#160;";
49342         cellText.innerHTML = val;
49343         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49344         this.syncRowHeights(rowIndex, rowIndex);
49345     },
49346
49347     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49348         var maxWidth = 0;
49349         if(this.grid.autoSizeHeaders){
49350             var h = this.getHeaderCellMeasure(colIndex);
49351             maxWidth = Math.max(maxWidth, h.scrollWidth);
49352         }
49353         var tb, index;
49354         if(this.cm.isLocked(colIndex)){
49355             tb = this.getLockedTable();
49356             index = colIndex;
49357         }else{
49358             tb = this.getBodyTable();
49359             index = colIndex - this.cm.getLockedCount();
49360         }
49361         if(tb && tb.rows){
49362             var rows = tb.rows;
49363             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49364             for(var i = 0; i < stopIndex; i++){
49365                 var cell = rows[i].childNodes[index].firstChild;
49366                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49367             }
49368         }
49369         return maxWidth + /*margin for error in IE*/ 5;
49370     },
49371     /**
49372      * Autofit a column to its content.
49373      * @param {Number} colIndex
49374      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49375      */
49376      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49377          if(this.cm.isHidden(colIndex)){
49378              return; // can't calc a hidden column
49379          }
49380         if(forceMinSize){
49381             var cid = this.cm.getColumnId(colIndex);
49382             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49383            if(this.grid.autoSizeHeaders){
49384                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49385            }
49386         }
49387         var newWidth = this.calcColumnWidth(colIndex);
49388         this.cm.setColumnWidth(colIndex,
49389             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49390         if(!suppressEvent){
49391             this.grid.fireEvent("columnresize", colIndex, newWidth);
49392         }
49393     },
49394
49395     /**
49396      * Autofits all columns to their content and then expands to fit any extra space in the grid
49397      */
49398      autoSizeColumns : function(){
49399         var cm = this.grid.colModel;
49400         var colCount = cm.getColumnCount();
49401         for(var i = 0; i < colCount; i++){
49402             this.autoSizeColumn(i, true, true);
49403         }
49404         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49405             this.fitColumns();
49406         }else{
49407             this.updateColumns();
49408             this.layout();
49409         }
49410     },
49411
49412     /**
49413      * Autofits all columns to the grid's width proportionate with their current size
49414      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49415      */
49416     fitColumns : function(reserveScrollSpace){
49417         var cm = this.grid.colModel;
49418         var colCount = cm.getColumnCount();
49419         var cols = [];
49420         var width = 0;
49421         var i, w;
49422         for (i = 0; i < colCount; i++){
49423             if(!cm.isHidden(i) && !cm.isFixed(i)){
49424                 w = cm.getColumnWidth(i);
49425                 cols.push(i);
49426                 cols.push(w);
49427                 width += w;
49428             }
49429         }
49430         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49431         if(reserveScrollSpace){
49432             avail -= 17;
49433         }
49434         var frac = (avail - cm.getTotalWidth())/width;
49435         while (cols.length){
49436             w = cols.pop();
49437             i = cols.pop();
49438             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49439         }
49440         this.updateColumns();
49441         this.layout();
49442     },
49443
49444     onRowSelect : function(rowIndex){
49445         var row = this.getRowComposite(rowIndex);
49446         row.addClass("x-grid-row-selected");
49447     },
49448
49449     onRowDeselect : function(rowIndex){
49450         var row = this.getRowComposite(rowIndex);
49451         row.removeClass("x-grid-row-selected");
49452     },
49453
49454     onCellSelect : function(row, col){
49455         var cell = this.getCell(row, col);
49456         if(cell){
49457             Roo.fly(cell).addClass("x-grid-cell-selected");
49458         }
49459     },
49460
49461     onCellDeselect : function(row, col){
49462         var cell = this.getCell(row, col);
49463         if(cell){
49464             Roo.fly(cell).removeClass("x-grid-cell-selected");
49465         }
49466     },
49467
49468     updateHeaderSortState : function(){
49469         
49470         // sort state can be single { field: xxx, direction : yyy}
49471         // or   { xxx=>ASC , yyy : DESC ..... }
49472         
49473         var mstate = {};
49474         if (!this.ds.multiSort) { 
49475             var state = this.ds.getSortState();
49476             if(!state){
49477                 return;
49478             }
49479             mstate[state.field] = state.direction;
49480             // FIXME... - this is not used here.. but might be elsewhere..
49481             this.sortState = state;
49482             
49483         } else {
49484             mstate = this.ds.sortToggle;
49485         }
49486         //remove existing sort classes..
49487         
49488         var sc = this.sortClasses;
49489         var hds = this.el.select(this.headerSelector).removeClass(sc);
49490         
49491         for(var f in mstate) {
49492         
49493             var sortColumn = this.cm.findColumnIndex(f);
49494             
49495             if(sortColumn != -1){
49496                 var sortDir = mstate[f];        
49497                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49498             }
49499         }
49500         
49501          
49502         
49503     },
49504
49505
49506     handleHeaderClick : function(g, index){
49507         if(this.headersDisabled){
49508             return;
49509         }
49510         var dm = g.dataSource, cm = g.colModel;
49511         if(!cm.isSortable(index)){
49512             return;
49513         }
49514         g.stopEditing();
49515         
49516         if (dm.multiSort) {
49517             // update the sortOrder
49518             var so = [];
49519             for(var i = 0; i < cm.config.length; i++ ) {
49520                 
49521                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49522                     continue; // dont' bother, it's not in sort list or being set.
49523                 }
49524                 
49525                 so.push(cm.config[i].dataIndex);
49526             };
49527             dm.sortOrder = so;
49528         }
49529         
49530         
49531         dm.sort(cm.getDataIndex(index));
49532     },
49533
49534
49535     destroy : function(){
49536         if(this.colMenu){
49537             this.colMenu.removeAll();
49538             Roo.menu.MenuMgr.unregister(this.colMenu);
49539             this.colMenu.getEl().remove();
49540             delete this.colMenu;
49541         }
49542         if(this.hmenu){
49543             this.hmenu.removeAll();
49544             Roo.menu.MenuMgr.unregister(this.hmenu);
49545             this.hmenu.getEl().remove();
49546             delete this.hmenu;
49547         }
49548         if(this.grid.enableColumnMove){
49549             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49550             if(dds){
49551                 for(var dd in dds){
49552                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49553                         var elid = dds[dd].dragElId;
49554                         dds[dd].unreg();
49555                         Roo.get(elid).remove();
49556                     } else if(dds[dd].config.isTarget){
49557                         dds[dd].proxyTop.remove();
49558                         dds[dd].proxyBottom.remove();
49559                         dds[dd].unreg();
49560                     }
49561                     if(Roo.dd.DDM.locationCache[dd]){
49562                         delete Roo.dd.DDM.locationCache[dd];
49563                     }
49564                 }
49565                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49566             }
49567         }
49568         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49569         this.bind(null, null);
49570         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49571     },
49572
49573     handleLockChange : function(){
49574         this.refresh(true);
49575     },
49576
49577     onDenyColumnLock : function(){
49578
49579     },
49580
49581     onDenyColumnHide : function(){
49582
49583     },
49584
49585     handleHdMenuClick : function(item){
49586         var index = this.hdCtxIndex;
49587         var cm = this.cm, ds = this.ds;
49588         switch(item.id){
49589             case "asc":
49590                 ds.sort(cm.getDataIndex(index), "ASC");
49591                 break;
49592             case "desc":
49593                 ds.sort(cm.getDataIndex(index), "DESC");
49594                 break;
49595             case "lock":
49596                 var lc = cm.getLockedCount();
49597                 if(cm.getColumnCount(true) <= lc+1){
49598                     this.onDenyColumnLock();
49599                     return;
49600                 }
49601                 if(lc != index){
49602                     cm.setLocked(index, true, true);
49603                     cm.moveColumn(index, lc);
49604                     this.grid.fireEvent("columnmove", index, lc);
49605                 }else{
49606                     cm.setLocked(index, true);
49607                 }
49608             break;
49609             case "unlock":
49610                 var lc = cm.getLockedCount();
49611                 if((lc-1) != index){
49612                     cm.setLocked(index, false, true);
49613                     cm.moveColumn(index, lc-1);
49614                     this.grid.fireEvent("columnmove", index, lc-1);
49615                 }else{
49616                     cm.setLocked(index, false);
49617                 }
49618             break;
49619             default:
49620                 index = cm.getIndexById(item.id.substr(4));
49621                 if(index != -1){
49622                     if(item.checked && cm.getColumnCount(true) <= 1){
49623                         this.onDenyColumnHide();
49624                         return false;
49625                     }
49626                     cm.setHidden(index, item.checked);
49627                 }
49628         }
49629         return true;
49630     },
49631
49632     beforeColMenuShow : function(){
49633         var cm = this.cm,  colCount = cm.getColumnCount();
49634         this.colMenu.removeAll();
49635         for(var i = 0; i < colCount; i++){
49636             this.colMenu.add(new Roo.menu.CheckItem({
49637                 id: "col-"+cm.getColumnId(i),
49638                 text: cm.getColumnHeader(i),
49639                 checked: !cm.isHidden(i),
49640                 hideOnClick:false
49641             }));
49642         }
49643     },
49644
49645     handleHdCtx : function(g, index, e){
49646         e.stopEvent();
49647         var hd = this.getHeaderCell(index);
49648         this.hdCtxIndex = index;
49649         var ms = this.hmenu.items, cm = this.cm;
49650         ms.get("asc").setDisabled(!cm.isSortable(index));
49651         ms.get("desc").setDisabled(!cm.isSortable(index));
49652         if(this.grid.enableColLock !== false){
49653             ms.get("lock").setDisabled(cm.isLocked(index));
49654             ms.get("unlock").setDisabled(!cm.isLocked(index));
49655         }
49656         this.hmenu.show(hd, "tl-bl");
49657     },
49658
49659     handleHdOver : function(e){
49660         var hd = this.findHeaderCell(e.getTarget());
49661         if(hd && !this.headersDisabled){
49662             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49663                this.fly(hd).addClass("x-grid-hd-over");
49664             }
49665         }
49666     },
49667
49668     handleHdOut : function(e){
49669         var hd = this.findHeaderCell(e.getTarget());
49670         if(hd){
49671             this.fly(hd).removeClass("x-grid-hd-over");
49672         }
49673     },
49674
49675     handleSplitDblClick : function(e, t){
49676         var i = this.getCellIndex(t);
49677         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49678             this.autoSizeColumn(i, true);
49679             this.layout();
49680         }
49681     },
49682
49683     render : function(){
49684
49685         var cm = this.cm;
49686         var colCount = cm.getColumnCount();
49687
49688         if(this.grid.monitorWindowResize === true){
49689             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49690         }
49691         var header = this.renderHeaders();
49692         var body = this.templates.body.apply({rows:""});
49693         var html = this.templates.master.apply({
49694             lockedBody: body,
49695             body: body,
49696             lockedHeader: header[0],
49697             header: header[1]
49698         });
49699
49700         //this.updateColumns();
49701
49702         this.grid.getGridEl().dom.innerHTML = html;
49703
49704         this.initElements();
49705         
49706         // a kludge to fix the random scolling effect in webkit
49707         this.el.on("scroll", function() {
49708             this.el.dom.scrollTop=0; // hopefully not recursive..
49709         },this);
49710
49711         this.scroller.on("scroll", this.handleScroll, this);
49712         this.lockedBody.on("mousewheel", this.handleWheel, this);
49713         this.mainBody.on("mousewheel", this.handleWheel, this);
49714
49715         this.mainHd.on("mouseover", this.handleHdOver, this);
49716         this.mainHd.on("mouseout", this.handleHdOut, this);
49717         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49718                 {delegate: "."+this.splitClass});
49719
49720         this.lockedHd.on("mouseover", this.handleHdOver, this);
49721         this.lockedHd.on("mouseout", this.handleHdOut, this);
49722         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49723                 {delegate: "."+this.splitClass});
49724
49725         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49726             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49727         }
49728
49729         this.updateSplitters();
49730
49731         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49732             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49733             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49734         }
49735
49736         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49737             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49738             this.hmenu.add(
49739                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49740                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49741             );
49742             if(this.grid.enableColLock !== false){
49743                 this.hmenu.add('-',
49744                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49745                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49746                 );
49747             }
49748             if(this.grid.enableColumnHide !== false){
49749
49750                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49751                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49752                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49753
49754                 this.hmenu.add('-',
49755                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49756                 );
49757             }
49758             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49759
49760             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49761         }
49762
49763         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49764             this.dd = new Roo.grid.GridDragZone(this.grid, {
49765                 ddGroup : this.grid.ddGroup || 'GridDD'
49766             });
49767         }
49768
49769         /*
49770         for(var i = 0; i < colCount; i++){
49771             if(cm.isHidden(i)){
49772                 this.hideColumn(i);
49773             }
49774             if(cm.config[i].align){
49775                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49776                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49777             }
49778         }*/
49779         
49780         this.updateHeaderSortState();
49781
49782         this.beforeInitialResize();
49783         this.layout(true);
49784
49785         // two part rendering gives faster view to the user
49786         this.renderPhase2.defer(1, this);
49787     },
49788
49789     renderPhase2 : function(){
49790         // render the rows now
49791         this.refresh();
49792         if(this.grid.autoSizeColumns){
49793             this.autoSizeColumns();
49794         }
49795     },
49796
49797     beforeInitialResize : function(){
49798
49799     },
49800
49801     onColumnSplitterMoved : function(i, w){
49802         this.userResized = true;
49803         var cm = this.grid.colModel;
49804         cm.setColumnWidth(i, w, true);
49805         var cid = cm.getColumnId(i);
49806         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49807         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49808         this.updateSplitters();
49809         this.layout();
49810         this.grid.fireEvent("columnresize", i, w);
49811     },
49812
49813     syncRowHeights : function(startIndex, endIndex){
49814         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49815             startIndex = startIndex || 0;
49816             var mrows = this.getBodyTable().rows;
49817             var lrows = this.getLockedTable().rows;
49818             var len = mrows.length-1;
49819             endIndex = Math.min(endIndex || len, len);
49820             for(var i = startIndex; i <= endIndex; i++){
49821                 var m = mrows[i], l = lrows[i];
49822                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49823                 m.style.height = l.style.height = h + "px";
49824             }
49825         }
49826     },
49827
49828     layout : function(initialRender, is2ndPass){
49829         var g = this.grid;
49830         var auto = g.autoHeight;
49831         var scrollOffset = 16;
49832         var c = g.getGridEl(), cm = this.cm,
49833                 expandCol = g.autoExpandColumn,
49834                 gv = this;
49835         //c.beginMeasure();
49836
49837         if(!c.dom.offsetWidth){ // display:none?
49838             if(initialRender){
49839                 this.lockedWrap.show();
49840                 this.mainWrap.show();
49841             }
49842             return;
49843         }
49844
49845         var hasLock = this.cm.isLocked(0);
49846
49847         var tbh = this.headerPanel.getHeight();
49848         var bbh = this.footerPanel.getHeight();
49849
49850         if(auto){
49851             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49852             var newHeight = ch + c.getBorderWidth("tb");
49853             if(g.maxHeight){
49854                 newHeight = Math.min(g.maxHeight, newHeight);
49855             }
49856             c.setHeight(newHeight);
49857         }
49858
49859         if(g.autoWidth){
49860             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49861         }
49862
49863         var s = this.scroller;
49864
49865         var csize = c.getSize(true);
49866
49867         this.el.setSize(csize.width, csize.height);
49868
49869         this.headerPanel.setWidth(csize.width);
49870         this.footerPanel.setWidth(csize.width);
49871
49872         var hdHeight = this.mainHd.getHeight();
49873         var vw = csize.width;
49874         var vh = csize.height - (tbh + bbh);
49875
49876         s.setSize(vw, vh);
49877
49878         var bt = this.getBodyTable();
49879         var ltWidth = hasLock ?
49880                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49881
49882         var scrollHeight = bt.offsetHeight;
49883         var scrollWidth = ltWidth + bt.offsetWidth;
49884         var vscroll = false, hscroll = false;
49885
49886         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49887
49888         var lw = this.lockedWrap, mw = this.mainWrap;
49889         var lb = this.lockedBody, mb = this.mainBody;
49890
49891         setTimeout(function(){
49892             var t = s.dom.offsetTop;
49893             var w = s.dom.clientWidth,
49894                 h = s.dom.clientHeight;
49895
49896             lw.setTop(t);
49897             lw.setSize(ltWidth, h);
49898
49899             mw.setLeftTop(ltWidth, t);
49900             mw.setSize(w-ltWidth, h);
49901
49902             lb.setHeight(h-hdHeight);
49903             mb.setHeight(h-hdHeight);
49904
49905             if(is2ndPass !== true && !gv.userResized && expandCol){
49906                 // high speed resize without full column calculation
49907                 
49908                 var ci = cm.getIndexById(expandCol);
49909                 if (ci < 0) {
49910                     ci = cm.findColumnIndex(expandCol);
49911                 }
49912                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49913                 var expandId = cm.getColumnId(ci);
49914                 var  tw = cm.getTotalWidth(false);
49915                 var currentWidth = cm.getColumnWidth(ci);
49916                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49917                 if(currentWidth != cw){
49918                     cm.setColumnWidth(ci, cw, true);
49919                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49920                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49921                     gv.updateSplitters();
49922                     gv.layout(false, true);
49923                 }
49924             }
49925
49926             if(initialRender){
49927                 lw.show();
49928                 mw.show();
49929             }
49930             //c.endMeasure();
49931         }, 10);
49932     },
49933
49934     onWindowResize : function(){
49935         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49936             return;
49937         }
49938         this.layout();
49939     },
49940
49941     appendFooter : function(parentEl){
49942         return null;
49943     },
49944
49945     sortAscText : "Sort Ascending",
49946     sortDescText : "Sort Descending",
49947     lockText : "Lock Column",
49948     unlockText : "Unlock Column",
49949     columnsText : "Columns"
49950 });
49951
49952
49953 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49954     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49955     this.proxy.el.addClass('x-grid3-col-dd');
49956 };
49957
49958 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49959     handleMouseDown : function(e){
49960
49961     },
49962
49963     callHandleMouseDown : function(e){
49964         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49965     }
49966 });
49967 /*
49968  * Based on:
49969  * Ext JS Library 1.1.1
49970  * Copyright(c) 2006-2007, Ext JS, LLC.
49971  *
49972  * Originally Released Under LGPL - original licence link has changed is not relivant.
49973  *
49974  * Fork - LGPL
49975  * <script type="text/javascript">
49976  */
49977  
49978 // private
49979 // This is a support class used internally by the Grid components
49980 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49981     this.grid = grid;
49982     this.view = grid.getView();
49983     this.proxy = this.view.resizeProxy;
49984     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49985         "gridSplitters" + this.grid.getGridEl().id, {
49986         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49987     });
49988     this.setHandleElId(Roo.id(hd));
49989     this.setOuterHandleElId(Roo.id(hd2));
49990     this.scroll = false;
49991 };
49992 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49993     fly: Roo.Element.fly,
49994
49995     b4StartDrag : function(x, y){
49996         this.view.headersDisabled = true;
49997         this.proxy.setHeight(this.view.mainWrap.getHeight());
49998         var w = this.cm.getColumnWidth(this.cellIndex);
49999         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50000         this.resetConstraints();
50001         this.setXConstraint(minw, 1000);
50002         this.setYConstraint(0, 0);
50003         this.minX = x - minw;
50004         this.maxX = x + 1000;
50005         this.startPos = x;
50006         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50007     },
50008
50009
50010     handleMouseDown : function(e){
50011         ev = Roo.EventObject.setEvent(e);
50012         var t = this.fly(ev.getTarget());
50013         if(t.hasClass("x-grid-split")){
50014             this.cellIndex = this.view.getCellIndex(t.dom);
50015             this.split = t.dom;
50016             this.cm = this.grid.colModel;
50017             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50018                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50019             }
50020         }
50021     },
50022
50023     endDrag : function(e){
50024         this.view.headersDisabled = false;
50025         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50026         var diff = endX - this.startPos;
50027         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50028     },
50029
50030     autoOffset : function(){
50031         this.setDelta(0,0);
50032     }
50033 });/*
50034  * Based on:
50035  * Ext JS Library 1.1.1
50036  * Copyright(c) 2006-2007, Ext JS, LLC.
50037  *
50038  * Originally Released Under LGPL - original licence link has changed is not relivant.
50039  *
50040  * Fork - LGPL
50041  * <script type="text/javascript">
50042  */
50043  
50044 // private
50045 // This is a support class used internally by the Grid components
50046 Roo.grid.GridDragZone = function(grid, config){
50047     this.view = grid.getView();
50048     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50049     if(this.view.lockedBody){
50050         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50051         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50052     }
50053     this.scroll = false;
50054     this.grid = grid;
50055     this.ddel = document.createElement('div');
50056     this.ddel.className = 'x-grid-dd-wrap';
50057 };
50058
50059 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50060     ddGroup : "GridDD",
50061
50062     getDragData : function(e){
50063         var t = Roo.lib.Event.getTarget(e);
50064         var rowIndex = this.view.findRowIndex(t);
50065         if(rowIndex !== false){
50066             var sm = this.grid.selModel;
50067             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50068               //  sm.mouseDown(e, t);
50069             //}
50070             if (e.hasModifier()){
50071                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50072             }
50073             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50074         }
50075         return false;
50076     },
50077
50078     onInitDrag : function(e){
50079         var data = this.dragData;
50080         this.ddel.innerHTML = this.grid.getDragDropText();
50081         this.proxy.update(this.ddel);
50082         // fire start drag?
50083     },
50084
50085     afterRepair : function(){
50086         this.dragging = false;
50087     },
50088
50089     getRepairXY : function(e, data){
50090         return false;
50091     },
50092
50093     onEndDrag : function(data, e){
50094         // fire end drag?
50095     },
50096
50097     onValidDrop : function(dd, e, id){
50098         // fire drag drop?
50099         this.hideProxy();
50100     },
50101
50102     beforeInvalidDrop : function(e, id){
50103
50104     }
50105 });/*
50106  * Based on:
50107  * Ext JS Library 1.1.1
50108  * Copyright(c) 2006-2007, Ext JS, LLC.
50109  *
50110  * Originally Released Under LGPL - original licence link has changed is not relivant.
50111  *
50112  * Fork - LGPL
50113  * <script type="text/javascript">
50114  */
50115  
50116
50117 /**
50118  * @class Roo.grid.ColumnModel
50119  * @extends Roo.util.Observable
50120  * This is the default implementation of a ColumnModel used by the Grid. It defines
50121  * the columns in the grid.
50122  * <br>Usage:<br>
50123  <pre><code>
50124  var colModel = new Roo.grid.ColumnModel([
50125         {header: "Ticker", width: 60, sortable: true, locked: true},
50126         {header: "Company Name", width: 150, sortable: true},
50127         {header: "Market Cap.", width: 100, sortable: true},
50128         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50129         {header: "Employees", width: 100, sortable: true, resizable: false}
50130  ]);
50131  </code></pre>
50132  * <p>
50133  
50134  * The config options listed for this class are options which may appear in each
50135  * individual column definition.
50136  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50137  * @constructor
50138  * @param {Object} config An Array of column config objects. See this class's
50139  * config objects for details.
50140 */
50141 Roo.grid.ColumnModel = function(config){
50142         /**
50143      * The config passed into the constructor
50144      */
50145     this.config = config;
50146     this.lookup = {};
50147
50148     // if no id, create one
50149     // if the column does not have a dataIndex mapping,
50150     // map it to the order it is in the config
50151     for(var i = 0, len = config.length; i < len; i++){
50152         var c = config[i];
50153         if(typeof c.dataIndex == "undefined"){
50154             c.dataIndex = i;
50155         }
50156         if(typeof c.renderer == "string"){
50157             c.renderer = Roo.util.Format[c.renderer];
50158         }
50159         if(typeof c.id == "undefined"){
50160             c.id = Roo.id();
50161         }
50162         if(c.editor && c.editor.xtype){
50163             c.editor  = Roo.factory(c.editor, Roo.grid);
50164         }
50165         if(c.editor && c.editor.isFormField){
50166             c.editor = new Roo.grid.GridEditor(c.editor);
50167         }
50168         this.lookup[c.id] = c;
50169     }
50170
50171     /**
50172      * The width of columns which have no width specified (defaults to 100)
50173      * @type Number
50174      */
50175     this.defaultWidth = 100;
50176
50177     /**
50178      * Default sortable of columns which have no sortable specified (defaults to false)
50179      * @type Boolean
50180      */
50181     this.defaultSortable = false;
50182
50183     this.addEvents({
50184         /**
50185              * @event widthchange
50186              * Fires when the width of a column changes.
50187              * @param {ColumnModel} this
50188              * @param {Number} columnIndex The column index
50189              * @param {Number} newWidth The new width
50190              */
50191             "widthchange": true,
50192         /**
50193              * @event headerchange
50194              * Fires when the text of a header changes.
50195              * @param {ColumnModel} this
50196              * @param {Number} columnIndex The column index
50197              * @param {Number} newText The new header text
50198              */
50199             "headerchange": true,
50200         /**
50201              * @event hiddenchange
50202              * Fires when a column is hidden or "unhidden".
50203              * @param {ColumnModel} this
50204              * @param {Number} columnIndex The column index
50205              * @param {Boolean} hidden true if hidden, false otherwise
50206              */
50207             "hiddenchange": true,
50208             /**
50209          * @event columnmoved
50210          * Fires when a column is moved.
50211          * @param {ColumnModel} this
50212          * @param {Number} oldIndex
50213          * @param {Number} newIndex
50214          */
50215         "columnmoved" : true,
50216         /**
50217          * @event columlockchange
50218          * Fires when a column's locked state is changed
50219          * @param {ColumnModel} this
50220          * @param {Number} colIndex
50221          * @param {Boolean} locked true if locked
50222          */
50223         "columnlockchange" : true
50224     });
50225     Roo.grid.ColumnModel.superclass.constructor.call(this);
50226 };
50227 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50228     /**
50229      * @cfg {String} header The header text to display in the Grid view.
50230      */
50231     /**
50232      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50233      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50234      * specified, the column's index is used as an index into the Record's data Array.
50235      */
50236     /**
50237      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50238      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50239      */
50240     /**
50241      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50242      * Defaults to the value of the {@link #defaultSortable} property.
50243      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50244      */
50245     /**
50246      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50247      */
50248     /**
50249      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50250      */
50251     /**
50252      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50253      */
50254     /**
50255      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50256      */
50257     /**
50258      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50259      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50260      * default renderer uses the raw data value.
50261      */
50262        /**
50263      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50264      */
50265     /**
50266      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50267      */
50268
50269     /**
50270      * Returns the id of the column at the specified index.
50271      * @param {Number} index The column index
50272      * @return {String} the id
50273      */
50274     getColumnId : function(index){
50275         return this.config[index].id;
50276     },
50277
50278     /**
50279      * Returns the column for a specified id.
50280      * @param {String} id The column id
50281      * @return {Object} the column
50282      */
50283     getColumnById : function(id){
50284         return this.lookup[id];
50285     },
50286
50287     
50288     /**
50289      * Returns the column for a specified dataIndex.
50290      * @param {String} dataIndex The column dataIndex
50291      * @return {Object|Boolean} the column or false if not found
50292      */
50293     getColumnByDataIndex: function(dataIndex){
50294         var index = this.findColumnIndex(dataIndex);
50295         return index > -1 ? this.config[index] : false;
50296     },
50297     
50298     /**
50299      * Returns the index for a specified column id.
50300      * @param {String} id The column id
50301      * @return {Number} the index, or -1 if not found
50302      */
50303     getIndexById : function(id){
50304         for(var i = 0, len = this.config.length; i < len; i++){
50305             if(this.config[i].id == id){
50306                 return i;
50307             }
50308         }
50309         return -1;
50310     },
50311     
50312     /**
50313      * Returns the index for a specified column dataIndex.
50314      * @param {String} dataIndex The column dataIndex
50315      * @return {Number} the index, or -1 if not found
50316      */
50317     
50318     findColumnIndex : function(dataIndex){
50319         for(var i = 0, len = this.config.length; i < len; i++){
50320             if(this.config[i].dataIndex == dataIndex){
50321                 return i;
50322             }
50323         }
50324         return -1;
50325     },
50326     
50327     
50328     moveColumn : function(oldIndex, newIndex){
50329         var c = this.config[oldIndex];
50330         this.config.splice(oldIndex, 1);
50331         this.config.splice(newIndex, 0, c);
50332         this.dataMap = null;
50333         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50334     },
50335
50336     isLocked : function(colIndex){
50337         return this.config[colIndex].locked === true;
50338     },
50339
50340     setLocked : function(colIndex, value, suppressEvent){
50341         if(this.isLocked(colIndex) == value){
50342             return;
50343         }
50344         this.config[colIndex].locked = value;
50345         if(!suppressEvent){
50346             this.fireEvent("columnlockchange", this, colIndex, value);
50347         }
50348     },
50349
50350     getTotalLockedWidth : function(){
50351         var totalWidth = 0;
50352         for(var i = 0; i < this.config.length; i++){
50353             if(this.isLocked(i) && !this.isHidden(i)){
50354                 this.totalWidth += this.getColumnWidth(i);
50355             }
50356         }
50357         return totalWidth;
50358     },
50359
50360     getLockedCount : function(){
50361         for(var i = 0, len = this.config.length; i < len; i++){
50362             if(!this.isLocked(i)){
50363                 return i;
50364             }
50365         }
50366     },
50367
50368     /**
50369      * Returns the number of columns.
50370      * @return {Number}
50371      */
50372     getColumnCount : function(visibleOnly){
50373         if(visibleOnly === true){
50374             var c = 0;
50375             for(var i = 0, len = this.config.length; i < len; i++){
50376                 if(!this.isHidden(i)){
50377                     c++;
50378                 }
50379             }
50380             return c;
50381         }
50382         return this.config.length;
50383     },
50384
50385     /**
50386      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50387      * @param {Function} fn
50388      * @param {Object} scope (optional)
50389      * @return {Array} result
50390      */
50391     getColumnsBy : function(fn, scope){
50392         var r = [];
50393         for(var i = 0, len = this.config.length; i < len; i++){
50394             var c = this.config[i];
50395             if(fn.call(scope||this, c, i) === true){
50396                 r[r.length] = c;
50397             }
50398         }
50399         return r;
50400     },
50401
50402     /**
50403      * Returns true if the specified column is sortable.
50404      * @param {Number} col The column index
50405      * @return {Boolean}
50406      */
50407     isSortable : function(col){
50408         if(typeof this.config[col].sortable == "undefined"){
50409             return this.defaultSortable;
50410         }
50411         return this.config[col].sortable;
50412     },
50413
50414     /**
50415      * Returns the rendering (formatting) function defined for the column.
50416      * @param {Number} col The column index.
50417      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50418      */
50419     getRenderer : function(col){
50420         if(!this.config[col].renderer){
50421             return Roo.grid.ColumnModel.defaultRenderer;
50422         }
50423         return this.config[col].renderer;
50424     },
50425
50426     /**
50427      * Sets the rendering (formatting) function for a column.
50428      * @param {Number} col The column index
50429      * @param {Function} fn The function to use to process the cell's raw data
50430      * to return HTML markup for the grid view. The render function is called with
50431      * the following parameters:<ul>
50432      * <li>Data value.</li>
50433      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50434      * <li>css A CSS style string to apply to the table cell.</li>
50435      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50436      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50437      * <li>Row index</li>
50438      * <li>Column index</li>
50439      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50440      */
50441     setRenderer : function(col, fn){
50442         this.config[col].renderer = fn;
50443     },
50444
50445     /**
50446      * Returns the width for the specified column.
50447      * @param {Number} col The column index
50448      * @return {Number}
50449      */
50450     getColumnWidth : function(col){
50451         return this.config[col].width * 1 || this.defaultWidth;
50452     },
50453
50454     /**
50455      * Sets the width for a column.
50456      * @param {Number} col The column index
50457      * @param {Number} width The new width
50458      */
50459     setColumnWidth : function(col, width, suppressEvent){
50460         this.config[col].width = width;
50461         this.totalWidth = null;
50462         if(!suppressEvent){
50463              this.fireEvent("widthchange", this, col, width);
50464         }
50465     },
50466
50467     /**
50468      * Returns the total width of all columns.
50469      * @param {Boolean} includeHidden True to include hidden column widths
50470      * @return {Number}
50471      */
50472     getTotalWidth : function(includeHidden){
50473         if(!this.totalWidth){
50474             this.totalWidth = 0;
50475             for(var i = 0, len = this.config.length; i < len; i++){
50476                 if(includeHidden || !this.isHidden(i)){
50477                     this.totalWidth += this.getColumnWidth(i);
50478                 }
50479             }
50480         }
50481         return this.totalWidth;
50482     },
50483
50484     /**
50485      * Returns the header for the specified column.
50486      * @param {Number} col The column index
50487      * @return {String}
50488      */
50489     getColumnHeader : function(col){
50490         return this.config[col].header;
50491     },
50492
50493     /**
50494      * Sets the header for a column.
50495      * @param {Number} col The column index
50496      * @param {String} header The new header
50497      */
50498     setColumnHeader : function(col, header){
50499         this.config[col].header = header;
50500         this.fireEvent("headerchange", this, col, header);
50501     },
50502
50503     /**
50504      * Returns the tooltip for the specified column.
50505      * @param {Number} col The column index
50506      * @return {String}
50507      */
50508     getColumnTooltip : function(col){
50509             return this.config[col].tooltip;
50510     },
50511     /**
50512      * Sets the tooltip for a column.
50513      * @param {Number} col The column index
50514      * @param {String} tooltip The new tooltip
50515      */
50516     setColumnTooltip : function(col, tooltip){
50517             this.config[col].tooltip = tooltip;
50518     },
50519
50520     /**
50521      * Returns the dataIndex for the specified column.
50522      * @param {Number} col The column index
50523      * @return {Number}
50524      */
50525     getDataIndex : function(col){
50526         return this.config[col].dataIndex;
50527     },
50528
50529     /**
50530      * Sets the dataIndex for a column.
50531      * @param {Number} col The column index
50532      * @param {Number} dataIndex The new dataIndex
50533      */
50534     setDataIndex : function(col, dataIndex){
50535         this.config[col].dataIndex = dataIndex;
50536     },
50537
50538     
50539     
50540     /**
50541      * Returns true if the cell is editable.
50542      * @param {Number} colIndex The column index
50543      * @param {Number} rowIndex The row index
50544      * @return {Boolean}
50545      */
50546     isCellEditable : function(colIndex, rowIndex){
50547         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50548     },
50549
50550     /**
50551      * Returns the editor defined for the cell/column.
50552      * return false or null to disable editing.
50553      * @param {Number} colIndex The column index
50554      * @param {Number} rowIndex The row index
50555      * @return {Object}
50556      */
50557     getCellEditor : function(colIndex, rowIndex){
50558         return this.config[colIndex].editor;
50559     },
50560
50561     /**
50562      * Sets if a column is editable.
50563      * @param {Number} col The column index
50564      * @param {Boolean} editable True if the column is editable
50565      */
50566     setEditable : function(col, editable){
50567         this.config[col].editable = editable;
50568     },
50569
50570
50571     /**
50572      * Returns true if the column is hidden.
50573      * @param {Number} colIndex The column index
50574      * @return {Boolean}
50575      */
50576     isHidden : function(colIndex){
50577         return this.config[colIndex].hidden;
50578     },
50579
50580
50581     /**
50582      * Returns true if the column width cannot be changed
50583      */
50584     isFixed : function(colIndex){
50585         return this.config[colIndex].fixed;
50586     },
50587
50588     /**
50589      * Returns true if the column can be resized
50590      * @return {Boolean}
50591      */
50592     isResizable : function(colIndex){
50593         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50594     },
50595     /**
50596      * Sets if a column is hidden.
50597      * @param {Number} colIndex The column index
50598      * @param {Boolean} hidden True if the column is hidden
50599      */
50600     setHidden : function(colIndex, hidden){
50601         this.config[colIndex].hidden = hidden;
50602         this.totalWidth = null;
50603         this.fireEvent("hiddenchange", this, colIndex, hidden);
50604     },
50605
50606     /**
50607      * Sets the editor for a column.
50608      * @param {Number} col The column index
50609      * @param {Object} editor The editor object
50610      */
50611     setEditor : function(col, editor){
50612         this.config[col].editor = editor;
50613     }
50614 });
50615
50616 Roo.grid.ColumnModel.defaultRenderer = function(value){
50617         if(typeof value == "string" && value.length < 1){
50618             return "&#160;";
50619         }
50620         return value;
50621 };
50622
50623 // Alias for backwards compatibility
50624 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50625 /*
50626  * Based on:
50627  * Ext JS Library 1.1.1
50628  * Copyright(c) 2006-2007, Ext JS, LLC.
50629  *
50630  * Originally Released Under LGPL - original licence link has changed is not relivant.
50631  *
50632  * Fork - LGPL
50633  * <script type="text/javascript">
50634  */
50635
50636 /**
50637  * @class Roo.grid.AbstractSelectionModel
50638  * @extends Roo.util.Observable
50639  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50640  * implemented by descendant classes.  This class should not be directly instantiated.
50641  * @constructor
50642  */
50643 Roo.grid.AbstractSelectionModel = function(){
50644     this.locked = false;
50645     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50646 };
50647
50648 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50649     /** @ignore Called by the grid automatically. Do not call directly. */
50650     init : function(grid){
50651         this.grid = grid;
50652         this.initEvents();
50653     },
50654
50655     /**
50656      * Locks the selections.
50657      */
50658     lock : function(){
50659         this.locked = true;
50660     },
50661
50662     /**
50663      * Unlocks the selections.
50664      */
50665     unlock : function(){
50666         this.locked = false;
50667     },
50668
50669     /**
50670      * Returns true if the selections are locked.
50671      * @return {Boolean}
50672      */
50673     isLocked : function(){
50674         return this.locked;
50675     }
50676 });/*
50677  * Based on:
50678  * Ext JS Library 1.1.1
50679  * Copyright(c) 2006-2007, Ext JS, LLC.
50680  *
50681  * Originally Released Under LGPL - original licence link has changed is not relivant.
50682  *
50683  * Fork - LGPL
50684  * <script type="text/javascript">
50685  */
50686 /**
50687  * @extends Roo.grid.AbstractSelectionModel
50688  * @class Roo.grid.RowSelectionModel
50689  * The default SelectionModel used by {@link Roo.grid.Grid}.
50690  * It supports multiple selections and keyboard selection/navigation. 
50691  * @constructor
50692  * @param {Object} config
50693  */
50694 Roo.grid.RowSelectionModel = function(config){
50695     Roo.apply(this, config);
50696     this.selections = new Roo.util.MixedCollection(false, function(o){
50697         return o.id;
50698     });
50699
50700     this.last = false;
50701     this.lastActive = false;
50702
50703     this.addEvents({
50704         /**
50705              * @event selectionchange
50706              * Fires when the selection changes
50707              * @param {SelectionModel} this
50708              */
50709             "selectionchange" : true,
50710         /**
50711              * @event afterselectionchange
50712              * Fires after the selection changes (eg. by key press or clicking)
50713              * @param {SelectionModel} this
50714              */
50715             "afterselectionchange" : true,
50716         /**
50717              * @event beforerowselect
50718              * Fires when a row is selected being selected, return false to cancel.
50719              * @param {SelectionModel} this
50720              * @param {Number} rowIndex The selected index
50721              * @param {Boolean} keepExisting False if other selections will be cleared
50722              */
50723             "beforerowselect" : true,
50724         /**
50725              * @event rowselect
50726              * Fires when a row is selected.
50727              * @param {SelectionModel} this
50728              * @param {Number} rowIndex The selected index
50729              * @param {Roo.data.Record} r The record
50730              */
50731             "rowselect" : true,
50732         /**
50733              * @event rowdeselect
50734              * Fires when a row is deselected.
50735              * @param {SelectionModel} this
50736              * @param {Number} rowIndex The selected index
50737              */
50738         "rowdeselect" : true
50739     });
50740     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50741     this.locked = false;
50742 };
50743
50744 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50745     /**
50746      * @cfg {Boolean} singleSelect
50747      * True to allow selection of only one row at a time (defaults to false)
50748      */
50749     singleSelect : false,
50750
50751     // private
50752     initEvents : function(){
50753
50754         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50755             this.grid.on("mousedown", this.handleMouseDown, this);
50756         }else{ // allow click to work like normal
50757             this.grid.on("rowclick", this.handleDragableRowClick, this);
50758         }
50759
50760         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50761             "up" : function(e){
50762                 if(!e.shiftKey){
50763                     this.selectPrevious(e.shiftKey);
50764                 }else if(this.last !== false && this.lastActive !== false){
50765                     var last = this.last;
50766                     this.selectRange(this.last,  this.lastActive-1);
50767                     this.grid.getView().focusRow(this.lastActive);
50768                     if(last !== false){
50769                         this.last = last;
50770                     }
50771                 }else{
50772                     this.selectFirstRow();
50773                 }
50774                 this.fireEvent("afterselectionchange", this);
50775             },
50776             "down" : function(e){
50777                 if(!e.shiftKey){
50778                     this.selectNext(e.shiftKey);
50779                 }else if(this.last !== false && this.lastActive !== false){
50780                     var last = this.last;
50781                     this.selectRange(this.last,  this.lastActive+1);
50782                     this.grid.getView().focusRow(this.lastActive);
50783                     if(last !== false){
50784                         this.last = last;
50785                     }
50786                 }else{
50787                     this.selectFirstRow();
50788                 }
50789                 this.fireEvent("afterselectionchange", this);
50790             },
50791             scope: this
50792         });
50793
50794         var view = this.grid.view;
50795         view.on("refresh", this.onRefresh, this);
50796         view.on("rowupdated", this.onRowUpdated, this);
50797         view.on("rowremoved", this.onRemove, this);
50798     },
50799
50800     // private
50801     onRefresh : function(){
50802         var ds = this.grid.dataSource, i, v = this.grid.view;
50803         var s = this.selections;
50804         s.each(function(r){
50805             if((i = ds.indexOfId(r.id)) != -1){
50806                 v.onRowSelect(i);
50807             }else{
50808                 s.remove(r);
50809             }
50810         });
50811     },
50812
50813     // private
50814     onRemove : function(v, index, r){
50815         this.selections.remove(r);
50816     },
50817
50818     // private
50819     onRowUpdated : function(v, index, r){
50820         if(this.isSelected(r)){
50821             v.onRowSelect(index);
50822         }
50823     },
50824
50825     /**
50826      * Select records.
50827      * @param {Array} records The records to select
50828      * @param {Boolean} keepExisting (optional) True to keep existing selections
50829      */
50830     selectRecords : function(records, keepExisting){
50831         if(!keepExisting){
50832             this.clearSelections();
50833         }
50834         var ds = this.grid.dataSource;
50835         for(var i = 0, len = records.length; i < len; i++){
50836             this.selectRow(ds.indexOf(records[i]), true);
50837         }
50838     },
50839
50840     /**
50841      * Gets the number of selected rows.
50842      * @return {Number}
50843      */
50844     getCount : function(){
50845         return this.selections.length;
50846     },
50847
50848     /**
50849      * Selects the first row in the grid.
50850      */
50851     selectFirstRow : function(){
50852         this.selectRow(0);
50853     },
50854
50855     /**
50856      * Select the last row.
50857      * @param {Boolean} keepExisting (optional) True to keep existing selections
50858      */
50859     selectLastRow : function(keepExisting){
50860         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50861     },
50862
50863     /**
50864      * Selects the row immediately following the last selected row.
50865      * @param {Boolean} keepExisting (optional) True to keep existing selections
50866      */
50867     selectNext : function(keepExisting){
50868         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50869             this.selectRow(this.last+1, keepExisting);
50870             this.grid.getView().focusRow(this.last);
50871         }
50872     },
50873
50874     /**
50875      * Selects the row that precedes the last selected row.
50876      * @param {Boolean} keepExisting (optional) True to keep existing selections
50877      */
50878     selectPrevious : function(keepExisting){
50879         if(this.last){
50880             this.selectRow(this.last-1, keepExisting);
50881             this.grid.getView().focusRow(this.last);
50882         }
50883     },
50884
50885     /**
50886      * Returns the selected records
50887      * @return {Array} Array of selected records
50888      */
50889     getSelections : function(){
50890         return [].concat(this.selections.items);
50891     },
50892
50893     /**
50894      * Returns the first selected record.
50895      * @return {Record}
50896      */
50897     getSelected : function(){
50898         return this.selections.itemAt(0);
50899     },
50900
50901
50902     /**
50903      * Clears all selections.
50904      */
50905     clearSelections : function(fast){
50906         if(this.locked) return;
50907         if(fast !== true){
50908             var ds = this.grid.dataSource;
50909             var s = this.selections;
50910             s.each(function(r){
50911                 this.deselectRow(ds.indexOfId(r.id));
50912             }, this);
50913             s.clear();
50914         }else{
50915             this.selections.clear();
50916         }
50917         this.last = false;
50918     },
50919
50920
50921     /**
50922      * Selects all rows.
50923      */
50924     selectAll : function(){
50925         if(this.locked) return;
50926         this.selections.clear();
50927         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50928             this.selectRow(i, true);
50929         }
50930     },
50931
50932     /**
50933      * Returns True if there is a selection.
50934      * @return {Boolean}
50935      */
50936     hasSelection : function(){
50937         return this.selections.length > 0;
50938     },
50939
50940     /**
50941      * Returns True if the specified row is selected.
50942      * @param {Number/Record} record The record or index of the record to check
50943      * @return {Boolean}
50944      */
50945     isSelected : function(index){
50946         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50947         return (r && this.selections.key(r.id) ? true : false);
50948     },
50949
50950     /**
50951      * Returns True if the specified record id is selected.
50952      * @param {String} id The id of record to check
50953      * @return {Boolean}
50954      */
50955     isIdSelected : function(id){
50956         return (this.selections.key(id) ? true : false);
50957     },
50958
50959     // private
50960     handleMouseDown : function(e, t){
50961         var view = this.grid.getView(), rowIndex;
50962         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50963             return;
50964         };
50965         if(e.shiftKey && this.last !== false){
50966             var last = this.last;
50967             this.selectRange(last, rowIndex, e.ctrlKey);
50968             this.last = last; // reset the last
50969             view.focusRow(rowIndex);
50970         }else{
50971             var isSelected = this.isSelected(rowIndex);
50972             if(e.button !== 0 && isSelected){
50973                 view.focusRow(rowIndex);
50974             }else if(e.ctrlKey && isSelected){
50975                 this.deselectRow(rowIndex);
50976             }else if(!isSelected){
50977                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50978                 view.focusRow(rowIndex);
50979             }
50980         }
50981         this.fireEvent("afterselectionchange", this);
50982     },
50983     // private
50984     handleDragableRowClick :  function(grid, rowIndex, e) 
50985     {
50986         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50987             this.selectRow(rowIndex, false);
50988             grid.view.focusRow(rowIndex);
50989              this.fireEvent("afterselectionchange", this);
50990         }
50991     },
50992     
50993     /**
50994      * Selects multiple rows.
50995      * @param {Array} rows Array of the indexes of the row to select
50996      * @param {Boolean} keepExisting (optional) True to keep existing selections
50997      */
50998     selectRows : function(rows, keepExisting){
50999         if(!keepExisting){
51000             this.clearSelections();
51001         }
51002         for(var i = 0, len = rows.length; i < len; i++){
51003             this.selectRow(rows[i], true);
51004         }
51005     },
51006
51007     /**
51008      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51009      * @param {Number} startRow The index of the first row in the range
51010      * @param {Number} endRow The index of the last row in the range
51011      * @param {Boolean} keepExisting (optional) True to retain existing selections
51012      */
51013     selectRange : function(startRow, endRow, keepExisting){
51014         if(this.locked) return;
51015         if(!keepExisting){
51016             this.clearSelections();
51017         }
51018         if(startRow <= endRow){
51019             for(var i = startRow; i <= endRow; i++){
51020                 this.selectRow(i, true);
51021             }
51022         }else{
51023             for(var i = startRow; i >= endRow; i--){
51024                 this.selectRow(i, true);
51025             }
51026         }
51027     },
51028
51029     /**
51030      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51031      * @param {Number} startRow The index of the first row in the range
51032      * @param {Number} endRow The index of the last row in the range
51033      */
51034     deselectRange : function(startRow, endRow, preventViewNotify){
51035         if(this.locked) return;
51036         for(var i = startRow; i <= endRow; i++){
51037             this.deselectRow(i, preventViewNotify);
51038         }
51039     },
51040
51041     /**
51042      * Selects a row.
51043      * @param {Number} row The index of the row to select
51044      * @param {Boolean} keepExisting (optional) True to keep existing selections
51045      */
51046     selectRow : function(index, keepExisting, preventViewNotify){
51047         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51048         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51049             if(!keepExisting || this.singleSelect){
51050                 this.clearSelections();
51051             }
51052             var r = this.grid.dataSource.getAt(index);
51053             this.selections.add(r);
51054             this.last = this.lastActive = index;
51055             if(!preventViewNotify){
51056                 this.grid.getView().onRowSelect(index);
51057             }
51058             this.fireEvent("rowselect", this, index, r);
51059             this.fireEvent("selectionchange", this);
51060         }
51061     },
51062
51063     /**
51064      * Deselects a row.
51065      * @param {Number} row The index of the row to deselect
51066      */
51067     deselectRow : function(index, preventViewNotify){
51068         if(this.locked) return;
51069         if(this.last == index){
51070             this.last = false;
51071         }
51072         if(this.lastActive == index){
51073             this.lastActive = false;
51074         }
51075         var r = this.grid.dataSource.getAt(index);
51076         this.selections.remove(r);
51077         if(!preventViewNotify){
51078             this.grid.getView().onRowDeselect(index);
51079         }
51080         this.fireEvent("rowdeselect", this, index);
51081         this.fireEvent("selectionchange", this);
51082     },
51083
51084     // private
51085     restoreLast : function(){
51086         if(this._last){
51087             this.last = this._last;
51088         }
51089     },
51090
51091     // private
51092     acceptsNav : function(row, col, cm){
51093         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51094     },
51095
51096     // private
51097     onEditorKey : function(field, e){
51098         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51099         if(k == e.TAB){
51100             e.stopEvent();
51101             ed.completeEdit();
51102             if(e.shiftKey){
51103                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51104             }else{
51105                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51106             }
51107         }else if(k == e.ENTER && !e.ctrlKey){
51108             e.stopEvent();
51109             ed.completeEdit();
51110             if(e.shiftKey){
51111                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51112             }else{
51113                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51114             }
51115         }else if(k == e.ESC){
51116             ed.cancelEdit();
51117         }
51118         if(newCell){
51119             g.startEditing(newCell[0], newCell[1]);
51120         }
51121     }
51122 });/*
51123  * Based on:
51124  * Ext JS Library 1.1.1
51125  * Copyright(c) 2006-2007, Ext JS, LLC.
51126  *
51127  * Originally Released Under LGPL - original licence link has changed is not relivant.
51128  *
51129  * Fork - LGPL
51130  * <script type="text/javascript">
51131  */
51132 /**
51133  * @class Roo.grid.CellSelectionModel
51134  * @extends Roo.grid.AbstractSelectionModel
51135  * This class provides the basic implementation for cell selection in a grid.
51136  * @constructor
51137  * @param {Object} config The object containing the configuration of this model.
51138  */
51139 Roo.grid.CellSelectionModel = function(config){
51140     Roo.apply(this, config);
51141
51142     this.selection = null;
51143
51144     this.addEvents({
51145         /**
51146              * @event beforerowselect
51147              * Fires before 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             "beforecellselect" : true,
51153         /**
51154              * @event cellselect
51155              * Fires when a cell is selected.
51156              * @param {SelectionModel} this
51157              * @param {Number} rowIndex The selected row index
51158              * @param {Number} colIndex The selected cell index
51159              */
51160             "cellselect" : true,
51161         /**
51162              * @event selectionchange
51163              * Fires when the active selection changes.
51164              * @param {SelectionModel} this
51165              * @param {Object} selection null for no selection or an object (o) with two properties
51166                 <ul>
51167                 <li>o.record: the record object for the row the selection is in</li>
51168                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51169                 </ul>
51170              */
51171             "selectionchange" : true
51172     });
51173     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51174 };
51175
51176 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51177
51178     /** @ignore */
51179     initEvents : function(){
51180         this.grid.on("mousedown", this.handleMouseDown, this);
51181         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51182         var view = this.grid.view;
51183         view.on("refresh", this.onViewChange, this);
51184         view.on("rowupdated", this.onRowUpdated, this);
51185         view.on("beforerowremoved", this.clearSelections, this);
51186         view.on("beforerowsinserted", this.clearSelections, this);
51187         if(this.grid.isEditor){
51188             this.grid.on("beforeedit", this.beforeEdit,  this);
51189         }
51190     },
51191
51192         //private
51193     beforeEdit : function(e){
51194         this.select(e.row, e.column, false, true, e.record);
51195     },
51196
51197         //private
51198     onRowUpdated : function(v, index, r){
51199         if(this.selection && this.selection.record == r){
51200             v.onCellSelect(index, this.selection.cell[1]);
51201         }
51202     },
51203
51204         //private
51205     onViewChange : function(){
51206         this.clearSelections(true);
51207     },
51208
51209         /**
51210          * Returns the currently selected cell,.
51211          * @return {Array} The selected cell (row, column) or null if none selected.
51212          */
51213     getSelectedCell : function(){
51214         return this.selection ? this.selection.cell : null;
51215     },
51216
51217     /**
51218      * Clears all selections.
51219      * @param {Boolean} true to prevent the gridview from being notified about the change.
51220      */
51221     clearSelections : function(preventNotify){
51222         var s = this.selection;
51223         if(s){
51224             if(preventNotify !== true){
51225                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51226             }
51227             this.selection = null;
51228             this.fireEvent("selectionchange", this, null);
51229         }
51230     },
51231
51232     /**
51233      * Returns true if there is a selection.
51234      * @return {Boolean}
51235      */
51236     hasSelection : function(){
51237         return this.selection ? true : false;
51238     },
51239
51240     /** @ignore */
51241     handleMouseDown : function(e, t){
51242         var v = this.grid.getView();
51243         if(this.isLocked()){
51244             return;
51245         };
51246         var row = v.findRowIndex(t);
51247         var cell = v.findCellIndex(t);
51248         if(row !== false && cell !== false){
51249             this.select(row, cell);
51250         }
51251     },
51252
51253     /**
51254      * Selects a cell.
51255      * @param {Number} rowIndex
51256      * @param {Number} collIndex
51257      */
51258     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51259         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51260             this.clearSelections();
51261             r = r || this.grid.dataSource.getAt(rowIndex);
51262             this.selection = {
51263                 record : r,
51264                 cell : [rowIndex, colIndex]
51265             };
51266             if(!preventViewNotify){
51267                 var v = this.grid.getView();
51268                 v.onCellSelect(rowIndex, colIndex);
51269                 if(preventFocus !== true){
51270                     v.focusCell(rowIndex, colIndex);
51271                 }
51272             }
51273             this.fireEvent("cellselect", this, rowIndex, colIndex);
51274             this.fireEvent("selectionchange", this, this.selection);
51275         }
51276     },
51277
51278         //private
51279     isSelectable : function(rowIndex, colIndex, cm){
51280         return !cm.isHidden(colIndex);
51281     },
51282
51283     /** @ignore */
51284     handleKeyDown : function(e){
51285         //Roo.log('Cell Sel Model handleKeyDown');
51286         if(!e.isNavKeyPress()){
51287             return;
51288         }
51289         var g = this.grid, s = this.selection;
51290         if(!s){
51291             e.stopEvent();
51292             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51293             if(cell){
51294                 this.select(cell[0], cell[1]);
51295             }
51296             return;
51297         }
51298         var sm = this;
51299         var walk = function(row, col, step){
51300             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51301         };
51302         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51303         var newCell;
51304
51305         switch(k){
51306             case e.TAB:
51307                 // handled by onEditorKey
51308                 if (g.isEditor && g.editing) {
51309                     return;
51310                 }
51311                 if(e.shiftKey){
51312                      newCell = walk(r, c-1, -1);
51313                 }else{
51314                      newCell = walk(r, c+1, 1);
51315                 }
51316              break;
51317              case e.DOWN:
51318                  newCell = walk(r+1, c, 1);
51319              break;
51320              case e.UP:
51321                  newCell = walk(r-1, c, -1);
51322              break;
51323              case e.RIGHT:
51324                  newCell = walk(r, c+1, 1);
51325              break;
51326              case e.LEFT:
51327                  newCell = walk(r, c-1, -1);
51328              break;
51329              case e.ENTER:
51330                  if(g.isEditor && !g.editing){
51331                     g.startEditing(r, c);
51332                     e.stopEvent();
51333                     return;
51334                 }
51335              break;
51336         };
51337         if(newCell){
51338             this.select(newCell[0], newCell[1]);
51339             e.stopEvent();
51340         }
51341     },
51342
51343     acceptsNav : function(row, col, cm){
51344         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51345     },
51346     /**
51347      * Selects a cell.
51348      * @param {Number} field (not used) - as it's normally used as a listener
51349      * @param {Number} e - event - fake it by using
51350      *
51351      * var e = Roo.EventObjectImpl.prototype;
51352      * e.keyCode = e.TAB
51353      *
51354      * 
51355      */
51356     onEditorKey : function(field, e){
51357         
51358         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51359         ///Roo.log('onEditorKey' + k);
51360         if (!ed) {
51361             
51362             
51363             
51364         }
51365         if(k == e.TAB){
51366             if(e.shiftKey){
51367                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51368             }else{
51369                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51370             }
51371             
51372             e.stopEvent();
51373             
51374         }else if(k == e.ENTER &&  !e.ctrlKey){
51375             ed.completeEdit();
51376             e.stopEvent();
51377             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51378         }else if(k == e.ESC){
51379             ed.cancelEdit();
51380         }
51381         
51382         
51383         if(newCell){
51384             //Roo.log('next cell after edit');
51385             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51386         }
51387     }
51388 });/*
51389  * Based on:
51390  * Ext JS Library 1.1.1
51391  * Copyright(c) 2006-2007, Ext JS, LLC.
51392  *
51393  * Originally Released Under LGPL - original licence link has changed is not relivant.
51394  *
51395  * Fork - LGPL
51396  * <script type="text/javascript">
51397  */
51398  
51399 /**
51400  * @class Roo.grid.EditorGrid
51401  * @extends Roo.grid.Grid
51402  * Class for creating and editable grid.
51403  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51404  * The container MUST have some type of size defined for the grid to fill. The container will be 
51405  * automatically set to position relative if it isn't already.
51406  * @param {Object} dataSource The data model to bind to
51407  * @param {Object} colModel The column model with info about this grid's columns
51408  */
51409 Roo.grid.EditorGrid = function(container, config){
51410     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51411     this.getGridEl().addClass("xedit-grid");
51412
51413     if(!this.selModel){
51414         this.selModel = new Roo.grid.CellSelectionModel();
51415     }
51416
51417     this.activeEditor = null;
51418
51419         this.addEvents({
51420             /**
51421              * @event beforeedit
51422              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51423              * <ul style="padding:5px;padding-left:16px;">
51424              * <li>grid - This grid</li>
51425              * <li>record - The record being edited</li>
51426              * <li>field - The field name being edited</li>
51427              * <li>value - The value for the field being edited.</li>
51428              * <li>row - The grid row index</li>
51429              * <li>column - The grid column index</li>
51430              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51431              * </ul>
51432              * @param {Object} e An edit event (see above for description)
51433              */
51434             "beforeedit" : true,
51435             /**
51436              * @event afteredit
51437              * Fires after a cell is edited. <br />
51438              * <ul style="padding:5px;padding-left:16px;">
51439              * <li>grid - This grid</li>
51440              * <li>record - The record being edited</li>
51441              * <li>field - The field name being edited</li>
51442              * <li>value - The value being set</li>
51443              * <li>originalValue - The original value for the field, before the edit.</li>
51444              * <li>row - The grid row index</li>
51445              * <li>column - The grid column index</li>
51446              * </ul>
51447              * @param {Object} e An edit event (see above for description)
51448              */
51449             "afteredit" : true,
51450             /**
51451              * @event validateedit
51452              * Fires after a cell is edited, but before the value is set in the record. 
51453          * You can use this to modify the value being set in the field, Return false
51454              * to cancel the change. The edit event object has the following properties <br />
51455              * <ul style="padding:5px;padding-left:16px;">
51456          * <li>editor - This editor</li>
51457              * <li>grid - This grid</li>
51458              * <li>record - The record being edited</li>
51459              * <li>field - The field name being edited</li>
51460              * <li>value - The value being set</li>
51461              * <li>originalValue - The original value for the field, before the edit.</li>
51462              * <li>row - The grid row index</li>
51463              * <li>column - The grid column index</li>
51464              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51465              * </ul>
51466              * @param {Object} e An edit event (see above for description)
51467              */
51468             "validateedit" : true
51469         });
51470     this.on("bodyscroll", this.stopEditing,  this);
51471     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51472 };
51473
51474 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51475     /**
51476      * @cfg {Number} clicksToEdit
51477      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51478      */
51479     clicksToEdit: 2,
51480
51481     // private
51482     isEditor : true,
51483     // private
51484     trackMouseOver: false, // causes very odd FF errors
51485
51486     onCellDblClick : function(g, row, col){
51487         this.startEditing(row, col);
51488     },
51489
51490     onEditComplete : function(ed, value, startValue){
51491         this.editing = false;
51492         this.activeEditor = null;
51493         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51494         var r = ed.record;
51495         var field = this.colModel.getDataIndex(ed.col);
51496         var e = {
51497             grid: this,
51498             record: r,
51499             field: field,
51500             originalValue: startValue,
51501             value: value,
51502             row: ed.row,
51503             column: ed.col,
51504             cancel:false,
51505             editor: ed
51506         };
51507         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51508         cell.show();
51509           
51510         if(String(value) !== String(startValue)){
51511             
51512             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51513                 r.set(field, e.value);
51514                 // if we are dealing with a combo box..
51515                 // then we also set the 'name' colum to be the displayField
51516                 if (ed.field.displayField && ed.field.name) {
51517                     r.set(ed.field.name, ed.field.el.dom.value);
51518                 }
51519                 
51520                 delete e.cancel; //?? why!!!
51521                 this.fireEvent("afteredit", e);
51522             }
51523         } else {
51524             this.fireEvent("afteredit", e); // always fire it!
51525         }
51526         this.view.focusCell(ed.row, ed.col);
51527     },
51528
51529     /**
51530      * Starts editing the specified for the specified row/column
51531      * @param {Number} rowIndex
51532      * @param {Number} colIndex
51533      */
51534     startEditing : function(row, col){
51535         this.stopEditing();
51536         if(this.colModel.isCellEditable(col, row)){
51537             this.view.ensureVisible(row, col, true);
51538           
51539             var r = this.dataSource.getAt(row);
51540             var field = this.colModel.getDataIndex(col);
51541             var cell = Roo.get(this.view.getCell(row,col));
51542             var e = {
51543                 grid: this,
51544                 record: r,
51545                 field: field,
51546                 value: r.data[field],
51547                 row: row,
51548                 column: col,
51549                 cancel:false 
51550             };
51551             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51552                 this.editing = true;
51553                 var ed = this.colModel.getCellEditor(col, row);
51554                 
51555                 if (!ed) {
51556                     return;
51557                 }
51558                 if(!ed.rendered){
51559                     ed.render(ed.parentEl || document.body);
51560                 }
51561                 ed.field.reset();
51562                
51563                 cell.hide();
51564                 
51565                 (function(){ // complex but required for focus issues in safari, ie and opera
51566                     ed.row = row;
51567                     ed.col = col;
51568                     ed.record = r;
51569                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51570                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51571                     this.activeEditor = ed;
51572                     var v = r.data[field];
51573                     ed.startEdit(this.view.getCell(row, col), v);
51574                     // combo's with 'displayField and name set
51575                     if (ed.field.displayField && ed.field.name) {
51576                         ed.field.el.dom.value = r.data[ed.field.name];
51577                     }
51578                     
51579                     
51580                 }).defer(50, this);
51581             }
51582         }
51583     },
51584         
51585     /**
51586      * Stops any active editing
51587      */
51588     stopEditing : function(){
51589         if(this.activeEditor){
51590             this.activeEditor.completeEdit();
51591         }
51592         this.activeEditor = null;
51593     }
51594 });/*
51595  * Based on:
51596  * Ext JS Library 1.1.1
51597  * Copyright(c) 2006-2007, Ext JS, LLC.
51598  *
51599  * Originally Released Under LGPL - original licence link has changed is not relivant.
51600  *
51601  * Fork - LGPL
51602  * <script type="text/javascript">
51603  */
51604
51605 // private - not really -- you end up using it !
51606 // This is a support class used internally by the Grid components
51607
51608 /**
51609  * @class Roo.grid.GridEditor
51610  * @extends Roo.Editor
51611  * Class for creating and editable grid elements.
51612  * @param {Object} config any settings (must include field)
51613  */
51614 Roo.grid.GridEditor = function(field, config){
51615     if (!config && field.field) {
51616         config = field;
51617         field = Roo.factory(config.field, Roo.form);
51618     }
51619     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51620     field.monitorTab = false;
51621 };
51622
51623 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51624     
51625     /**
51626      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51627      */
51628     
51629     alignment: "tl-tl",
51630     autoSize: "width",
51631     hideEl : false,
51632     cls: "x-small-editor x-grid-editor",
51633     shim:false,
51634     shadow:"frame"
51635 });/*
51636  * Based on:
51637  * Ext JS Library 1.1.1
51638  * Copyright(c) 2006-2007, Ext JS, LLC.
51639  *
51640  * Originally Released Under LGPL - original licence link has changed is not relivant.
51641  *
51642  * Fork - LGPL
51643  * <script type="text/javascript">
51644  */
51645   
51646
51647   
51648 Roo.grid.PropertyRecord = Roo.data.Record.create([
51649     {name:'name',type:'string'},  'value'
51650 ]);
51651
51652
51653 Roo.grid.PropertyStore = function(grid, source){
51654     this.grid = grid;
51655     this.store = new Roo.data.Store({
51656         recordType : Roo.grid.PropertyRecord
51657     });
51658     this.store.on('update', this.onUpdate,  this);
51659     if(source){
51660         this.setSource(source);
51661     }
51662     Roo.grid.PropertyStore.superclass.constructor.call(this);
51663 };
51664
51665
51666
51667 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51668     setSource : function(o){
51669         this.source = o;
51670         this.store.removeAll();
51671         var data = [];
51672         for(var k in o){
51673             if(this.isEditableValue(o[k])){
51674                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51675             }
51676         }
51677         this.store.loadRecords({records: data}, {}, true);
51678     },
51679
51680     onUpdate : function(ds, record, type){
51681         if(type == Roo.data.Record.EDIT){
51682             var v = record.data['value'];
51683             var oldValue = record.modified['value'];
51684             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51685                 this.source[record.id] = v;
51686                 record.commit();
51687                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51688             }else{
51689                 record.reject();
51690             }
51691         }
51692     },
51693
51694     getProperty : function(row){
51695        return this.store.getAt(row);
51696     },
51697
51698     isEditableValue: function(val){
51699         if(val && val instanceof Date){
51700             return true;
51701         }else if(typeof val == 'object' || typeof val == 'function'){
51702             return false;
51703         }
51704         return true;
51705     },
51706
51707     setValue : function(prop, value){
51708         this.source[prop] = value;
51709         this.store.getById(prop).set('value', value);
51710     },
51711
51712     getSource : function(){
51713         return this.source;
51714     }
51715 });
51716
51717 Roo.grid.PropertyColumnModel = function(grid, store){
51718     this.grid = grid;
51719     var g = Roo.grid;
51720     g.PropertyColumnModel.superclass.constructor.call(this, [
51721         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51722         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51723     ]);
51724     this.store = store;
51725     this.bselect = Roo.DomHelper.append(document.body, {
51726         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51727             {tag: 'option', value: 'true', html: 'true'},
51728             {tag: 'option', value: 'false', html: 'false'}
51729         ]
51730     });
51731     Roo.id(this.bselect);
51732     var f = Roo.form;
51733     this.editors = {
51734         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51735         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51736         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51737         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51738         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51739     };
51740     this.renderCellDelegate = this.renderCell.createDelegate(this);
51741     this.renderPropDelegate = this.renderProp.createDelegate(this);
51742 };
51743
51744 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51745     
51746     
51747     nameText : 'Name',
51748     valueText : 'Value',
51749     
51750     dateFormat : 'm/j/Y',
51751     
51752     
51753     renderDate : function(dateVal){
51754         return dateVal.dateFormat(this.dateFormat);
51755     },
51756
51757     renderBool : function(bVal){
51758         return bVal ? 'true' : 'false';
51759     },
51760
51761     isCellEditable : function(colIndex, rowIndex){
51762         return colIndex == 1;
51763     },
51764
51765     getRenderer : function(col){
51766         return col == 1 ?
51767             this.renderCellDelegate : this.renderPropDelegate;
51768     },
51769
51770     renderProp : function(v){
51771         return this.getPropertyName(v);
51772     },
51773
51774     renderCell : function(val){
51775         var rv = val;
51776         if(val instanceof Date){
51777             rv = this.renderDate(val);
51778         }else if(typeof val == 'boolean'){
51779             rv = this.renderBool(val);
51780         }
51781         return Roo.util.Format.htmlEncode(rv);
51782     },
51783
51784     getPropertyName : function(name){
51785         var pn = this.grid.propertyNames;
51786         return pn && pn[name] ? pn[name] : name;
51787     },
51788
51789     getCellEditor : function(colIndex, rowIndex){
51790         var p = this.store.getProperty(rowIndex);
51791         var n = p.data['name'], val = p.data['value'];
51792         
51793         if(typeof(this.grid.customEditors[n]) == 'string'){
51794             return this.editors[this.grid.customEditors[n]];
51795         }
51796         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51797             return this.grid.customEditors[n];
51798         }
51799         if(val instanceof Date){
51800             return this.editors['date'];
51801         }else if(typeof val == 'number'){
51802             return this.editors['number'];
51803         }else if(typeof val == 'boolean'){
51804             return this.editors['boolean'];
51805         }else{
51806             return this.editors['string'];
51807         }
51808     }
51809 });
51810
51811 /**
51812  * @class Roo.grid.PropertyGrid
51813  * @extends Roo.grid.EditorGrid
51814  * This class represents the  interface of a component based property grid control.
51815  * <br><br>Usage:<pre><code>
51816  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51817       
51818  });
51819  // set any options
51820  grid.render();
51821  * </code></pre>
51822   
51823  * @constructor
51824  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51825  * The container MUST have some type of size defined for the grid to fill. The container will be
51826  * automatically set to position relative if it isn't already.
51827  * @param {Object} config A config object that sets properties on this grid.
51828  */
51829 Roo.grid.PropertyGrid = function(container, config){
51830     config = config || {};
51831     var store = new Roo.grid.PropertyStore(this);
51832     this.store = store;
51833     var cm = new Roo.grid.PropertyColumnModel(this, store);
51834     store.store.sort('name', 'ASC');
51835     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51836         ds: store.store,
51837         cm: cm,
51838         enableColLock:false,
51839         enableColumnMove:false,
51840         stripeRows:false,
51841         trackMouseOver: false,
51842         clicksToEdit:1
51843     }, config));
51844     this.getGridEl().addClass('x-props-grid');
51845     this.lastEditRow = null;
51846     this.on('columnresize', this.onColumnResize, this);
51847     this.addEvents({
51848          /**
51849              * @event beforepropertychange
51850              * Fires before a property changes (return false to stop?)
51851              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51852              * @param {String} id Record Id
51853              * @param {String} newval New Value
51854          * @param {String} oldval Old Value
51855              */
51856         "beforepropertychange": true,
51857         /**
51858              * @event propertychange
51859              * Fires after a property changes
51860              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51861              * @param {String} id Record Id
51862              * @param {String} newval New Value
51863          * @param {String} oldval Old Value
51864              */
51865         "propertychange": true
51866     });
51867     this.customEditors = this.customEditors || {};
51868 };
51869 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51870     
51871      /**
51872      * @cfg {Object} customEditors map of colnames=> custom editors.
51873      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51874      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51875      * false disables editing of the field.
51876          */
51877     
51878       /**
51879      * @cfg {Object} propertyNames map of property Names to their displayed value
51880          */
51881     
51882     render : function(){
51883         Roo.grid.PropertyGrid.superclass.render.call(this);
51884         this.autoSize.defer(100, this);
51885     },
51886
51887     autoSize : function(){
51888         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51889         if(this.view){
51890             this.view.fitColumns();
51891         }
51892     },
51893
51894     onColumnResize : function(){
51895         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51896         this.autoSize();
51897     },
51898     /**
51899      * Sets the data for the Grid
51900      * accepts a Key => Value object of all the elements avaiable.
51901      * @param {Object} data  to appear in grid.
51902      */
51903     setSource : function(source){
51904         this.store.setSource(source);
51905         //this.autoSize();
51906     },
51907     /**
51908      * Gets all the data from the grid.
51909      * @return {Object} data  data stored in grid
51910      */
51911     getSource : function(){
51912         return this.store.getSource();
51913     }
51914 });/*
51915  * Based on:
51916  * Ext JS Library 1.1.1
51917  * Copyright(c) 2006-2007, Ext JS, LLC.
51918  *
51919  * Originally Released Under LGPL - original licence link has changed is not relivant.
51920  *
51921  * Fork - LGPL
51922  * <script type="text/javascript">
51923  */
51924  
51925 /**
51926  * @class Roo.LoadMask
51927  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51928  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51929  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51930  * element's UpdateManager load indicator and will be destroyed after the initial load.
51931  * @constructor
51932  * Create a new LoadMask
51933  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51934  * @param {Object} config The config object
51935  */
51936 Roo.LoadMask = function(el, config){
51937     this.el = Roo.get(el);
51938     Roo.apply(this, config);
51939     if(this.store){
51940         this.store.on('beforeload', this.onBeforeLoad, this);
51941         this.store.on('load', this.onLoad, this);
51942         this.store.on('loadexception', this.onLoadException, this);
51943         this.removeMask = false;
51944     }else{
51945         var um = this.el.getUpdateManager();
51946         um.showLoadIndicator = false; // disable the default indicator
51947         um.on('beforeupdate', this.onBeforeLoad, this);
51948         um.on('update', this.onLoad, this);
51949         um.on('failure', this.onLoad, this);
51950         this.removeMask = true;
51951     }
51952 };
51953
51954 Roo.LoadMask.prototype = {
51955     /**
51956      * @cfg {Boolean} removeMask
51957      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51958      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51959      */
51960     /**
51961      * @cfg {String} msg
51962      * The text to display in a centered loading message box (defaults to 'Loading...')
51963      */
51964     msg : 'Loading...',
51965     /**
51966      * @cfg {String} msgCls
51967      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51968      */
51969     msgCls : 'x-mask-loading',
51970
51971     /**
51972      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51973      * @type Boolean
51974      */
51975     disabled: false,
51976
51977     /**
51978      * Disables the mask to prevent it from being displayed
51979      */
51980     disable : function(){
51981        this.disabled = true;
51982     },
51983
51984     /**
51985      * Enables the mask so that it can be displayed
51986      */
51987     enable : function(){
51988         this.disabled = false;
51989     },
51990     
51991     onLoadException : function()
51992     {
51993         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51994             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51995         }
51996         this.el.unmask(this.removeMask);
51997     },
51998     // private
51999     onLoad : function()
52000     {
52001         this.el.unmask(this.removeMask);
52002     },
52003
52004     // private
52005     onBeforeLoad : function(){
52006         if(!this.disabled){
52007             this.el.mask(this.msg, this.msgCls);
52008         }
52009     },
52010
52011     // private
52012     destroy : function(){
52013         if(this.store){
52014             this.store.un('beforeload', this.onBeforeLoad, this);
52015             this.store.un('load', this.onLoad, this);
52016             this.store.un('loadexception', this.onLoadException, this);
52017         }else{
52018             var um = this.el.getUpdateManager();
52019             um.un('beforeupdate', this.onBeforeLoad, this);
52020             um.un('update', this.onLoad, this);
52021             um.un('failure', this.onLoad, this);
52022         }
52023     }
52024 };/*
52025  * Based on:
52026  * Ext JS Library 1.1.1
52027  * Copyright(c) 2006-2007, Ext JS, LLC.
52028  *
52029  * Originally Released Under LGPL - original licence link has changed is not relivant.
52030  *
52031  * Fork - LGPL
52032  * <script type="text/javascript">
52033  */
52034 Roo.XTemplate = function(){
52035     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52036     var s = this.html;
52037
52038     s = ['<tpl>', s, '</tpl>'].join('');
52039
52040     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52041
52042     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52043     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52044     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52045     var m, id = 0;
52046     var tpls = [];
52047
52048     while(m = s.match(re)){
52049        var m2 = m[0].match(nameRe);
52050        var m3 = m[0].match(ifRe);
52051        var m4 = m[0].match(execRe);
52052        var exp = null, fn = null, exec = null;
52053        var name = m2 && m2[1] ? m2[1] : '';
52054        if(m3){
52055            exp = m3 && m3[1] ? m3[1] : null;
52056            if(exp){
52057                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52058            }
52059        }
52060        if(m4){
52061            exp = m4 && m4[1] ? m4[1] : null;
52062            if(exp){
52063                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52064            }
52065        }
52066        if(name){
52067            switch(name){
52068                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52069                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52070                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52071            }
52072        }
52073        tpls.push({
52074             id: id,
52075             target: name,
52076             exec: exec,
52077             test: fn,
52078             body: m[1]||''
52079         });
52080        s = s.replace(m[0], '{xtpl'+ id + '}');
52081        ++id;
52082     }
52083     for(var i = tpls.length-1; i >= 0; --i){
52084         this.compileTpl(tpls[i]);
52085     }
52086     this.master = tpls[tpls.length-1];
52087     this.tpls = tpls;
52088 };
52089 Roo.extend(Roo.XTemplate, Roo.Template, {
52090
52091     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52092
52093     applySubTemplate : function(id, values, parent){
52094         var t = this.tpls[id];
52095         if(t.test && !t.test.call(this, values, parent)){
52096             return '';
52097         }
52098         if(t.exec && t.exec.call(this, values, parent)){
52099             return '';
52100         }
52101         var vs = t.target ? t.target.call(this, values, parent) : values;
52102         parent = t.target ? values : parent;
52103         if(t.target && vs instanceof Array){
52104             var buf = [];
52105             for(var i = 0, len = vs.length; i < len; i++){
52106                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52107             }
52108             return buf.join('');
52109         }
52110         return t.compiled.call(this, vs, parent);
52111     },
52112
52113     compileTpl : function(tpl){
52114         var fm = Roo.util.Format;
52115         var useF = this.disableFormats !== true;
52116         var sep = Roo.isGecko ? "+" : ",";
52117         var fn = function(m, name, format, args){
52118             if(name.substr(0, 4) == 'xtpl'){
52119                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52120             }
52121             var v;
52122             if(name.indexOf('.') != -1){
52123                 v = name;
52124             }else{
52125                 v = "values['" + name + "']";
52126             }
52127             if(format && useF){
52128                 args = args ? ',' + args : "";
52129                 if(format.substr(0, 5) != "this."){
52130                     format = "fm." + format + '(';
52131                 }else{
52132                     format = 'this.call("'+ format.substr(5) + '", ';
52133                     args = ", values";
52134                 }
52135             }else{
52136                 args= ''; format = "("+v+" === undefined ? '' : ";
52137             }
52138             return "'"+ sep + format + v + args + ")"+sep+"'";
52139         };
52140         var body;
52141         // branched to use + in gecko and [].join() in others
52142         if(Roo.isGecko){
52143             body = "tpl.compiled = function(values, parent){ return '" +
52144                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52145                     "';};";
52146         }else{
52147             body = ["tpl.compiled = function(values, parent){ return ['"];
52148             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52149             body.push("'].join('');};");
52150             body = body.join('');
52151         }
52152         /** eval:var:zzzzzzz */
52153         eval(body);
52154         return this;
52155     },
52156
52157     applyTemplate : function(values){
52158         return this.master.compiled.call(this, values, {});
52159         var s = this.subs;
52160     },
52161
52162     apply : function(){
52163         return this.applyTemplate.apply(this, arguments);
52164     },
52165
52166     compile : function(){return this;}
52167 });
52168
52169 Roo.XTemplate.from = function(el){
52170     el = Roo.getDom(el);
52171     return new Roo.XTemplate(el.value || el.innerHTML);
52172 };/*
52173  * Original code for Roojs - LGPL
52174  * <script type="text/javascript">
52175  */
52176  
52177 /**
52178  * @class Roo.XComponent
52179  * A delayed Element creator...
52180  * Or a way to group chunks of interface together.
52181  * 
52182  * Mypart.xyx = new Roo.XComponent({
52183
52184     parent : 'Mypart.xyz', // empty == document.element.!!
52185     order : '001',
52186     name : 'xxxx'
52187     region : 'xxxx'
52188     disabled : function() {} 
52189      
52190     tree : function() { // return an tree of xtype declared components
52191         var MODULE = this;
52192         return 
52193         {
52194             xtype : 'NestedLayoutPanel',
52195             // technicall
52196         }
52197      ]
52198  *})
52199  *
52200  *
52201  * It can be used to build a big heiracy, with parent etc.
52202  * or you can just use this to render a single compoent to a dom element
52203  * MYPART.render(Roo.Element | String(id) | dom_element )
52204  * 
52205  * @extends Roo.util.Observable
52206  * @constructor
52207  * @param cfg {Object} configuration of component
52208  * 
52209  */
52210 Roo.XComponent = function(cfg) {
52211     Roo.apply(this, cfg);
52212     this.addEvents({ 
52213         /**
52214              * @event built
52215              * Fires when this the componnt is built
52216              * @param {Roo.XComponent} c the component
52217              */
52218         'built' : true,
52219         /**
52220              * @event buildcomplete
52221              * Fires on the top level element when all elements have been built
52222              * @param {Roo.XComponent} c the top level component.
52223          */
52224         'buildcomplete' : true
52225         
52226     });
52227     this.region = this.region || 'center'; // default..
52228     Roo.XComponent.register(this);
52229     this.modules = false;
52230     this.el = false; // where the layout goes..
52231     
52232     
52233 }
52234 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52235     /**
52236      * @property el
52237      * The created element (with Roo.factory())
52238      * @type {Roo.Layout}
52239      */
52240     el  : false,
52241     
52242     /**
52243      * @property el
52244      * for BC  - use el in new code
52245      * @type {Roo.Layout}
52246      */
52247     panel : false,
52248     
52249     /**
52250      * @property layout
52251      * for BC  - use el in new code
52252      * @type {Roo.Layout}
52253      */
52254     layout : false,
52255     
52256      /**
52257      * @cfg {Function|boolean} disabled
52258      * If this module is disabled by some rule, return true from the funtion
52259      */
52260     disabled : false,
52261     
52262     /**
52263      * @cfg {String} parent 
52264      * Name of parent element which it get xtype added to..
52265      */
52266     parent: false,
52267     
52268     /**
52269      * @cfg {String} order
52270      * Used to set the order in which elements are created (usefull for multiple tabs)
52271      */
52272     
52273     order : false,
52274     /**
52275      * @cfg {String} name
52276      * String to display while loading.
52277      */
52278     name : false,
52279     /**
52280      * @cfg {String} region
52281      * Region to render component to (defaults to center)
52282      */
52283     region : 'center',
52284     
52285     /**
52286      * @cfg {Array} items
52287      * A single item array - the first element is the root of the tree..
52288      * It's done this way to stay compatible with the Xtype system...
52289      */
52290     items : false,
52291     
52292     
52293      /**
52294      * render
52295      * render element to dom or tree
52296      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52297      */
52298     
52299     render : function(el)
52300     {
52301         
52302         el = el || false;
52303         var hp = this.parent ? 1 : 0;
52304         
52305         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52306             // if parent is a '#.....' string, then let's use that..
52307             var ename = this.parent.substr(1)
52308             this.parent = false;
52309             el = Roo.get(ename);
52310             if (!el) {
52311                 Roo.log("Warning - element can not be found :#" + ename );
52312                 return;
52313             }
52314         }
52315         
52316         
52317         if (!this.parent) {
52318             
52319             el = el ? Roo.get(el) : false;
52320             
52321             // it's a top level one..
52322             this.parent =  {
52323                 el : new Roo.BorderLayout(el || document.body, {
52324                 
52325                      center: {
52326                          titlebar: false,
52327                          autoScroll:false,
52328                          closeOnTab: true,
52329                          tabPosition: 'top',
52330                           //resizeTabs: true,
52331                          alwaysShowTabs: el && hp? false :  true,
52332                          hideTabs: el || !hp ? true :  false,
52333                          minTabWidth: 140
52334                      }
52335                  })
52336             }
52337         }
52338         
52339         
52340             
52341         var tree = this.tree();
52342         tree.region = tree.region || this.region;
52343         this.el = this.parent.el.addxtype(tree);
52344         this.fireEvent('built', this);
52345         
52346         this.panel = this.el;
52347         this.layout = this.panel.layout;    
52348          
52349     }
52350     
52351 });
52352
52353 Roo.apply(Roo.XComponent, {
52354     
52355     /**
52356      * @property  buildCompleted
52357      * True when the builder has completed building the interface.
52358      * @type Boolean
52359      */
52360     buildCompleted : false,
52361      
52362     /**
52363      * @property  topModule
52364      * the upper most module - uses document.element as it's constructor.
52365      * @type Object
52366      */
52367      
52368     topModule  : false,
52369       
52370     /**
52371      * @property  modules
52372      * array of modules to be created by registration system.
52373      * @type {Array} of Roo.XComponent
52374      */
52375     
52376     modules : [],
52377     /**
52378      * @property  elmodules
52379      * array of modules to be created by which use #ID 
52380      * @type {Array} of Roo.XComponent
52381      */
52382      
52383     elmodules : [],
52384
52385     
52386     /**
52387      * Register components to be built later.
52388      *
52389      * This solves the following issues
52390      * - Building is not done on page load, but after an authentication process has occured.
52391      * - Interface elements are registered on page load
52392      * - Parent Interface elements may not be loaded before child, so this handles that..
52393      * 
52394      *
52395      * example:
52396      * 
52397      * MyApp.register({
52398           order : '000001',
52399           module : 'Pman.Tab.projectMgr',
52400           region : 'center',
52401           parent : 'Pman.layout',
52402           disabled : false,  // or use a function..
52403         })
52404      
52405      * * @param {Object} details about module
52406      */
52407     register : function(obj) {
52408         this.modules.push(obj);
52409          
52410     },
52411     /**
52412      * convert a string to an object..
52413      * eg. 'AAA.BBB' -> finds AAA.BBB
52414
52415      */
52416     
52417     toObject : function(str)
52418     {
52419         if (!str || typeof(str) == 'object') {
52420             return str;
52421         }
52422         if (str.substring(0,1) == '#') {
52423             return str;
52424         }
52425
52426         var ar = str.split('.');
52427         var rt, o;
52428         rt = ar.shift();
52429             /** eval:var:o */
52430         try {
52431             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52432         } catch (e) {
52433             throw "Module not found : " + str;
52434         }
52435         
52436         if (o === false) {
52437             throw "Module not found : " + str;
52438         }
52439         Roo.each(ar, function(e) {
52440             if (typeof(o[e]) == 'undefined') {
52441                 throw "Module not found : " + str;
52442             }
52443             o = o[e];
52444         });
52445         
52446         return o;
52447         
52448     },
52449     
52450     
52451     /**
52452      * move modules into their correct place in the tree..
52453      * 
52454      */
52455     preBuild : function ()
52456     {
52457         var _t = this;
52458         Roo.each(this.modules , function (obj)
52459         {
52460             var opar = obj.parent;
52461             try { 
52462                 obj.parent = this.toObject(opar);
52463             } catch(e) {
52464                 Roo.log(e.toString());
52465                 return;
52466             }
52467             
52468             if (!obj.parent) {
52469                 this.topModule = obj;
52470                 return;
52471             }
52472             if (typeof(obj.parent) == 'string') {
52473                 this.elmodules.push(obj);
52474                 return;
52475             }
52476             if (obj.parent.constructor != Roo.XComponent) {
52477                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52478             }
52479             if (!obj.parent.modules) {
52480                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52481                     function(o) { return o.order + '' }
52482                 );
52483             }
52484             
52485             obj.parent.modules.add(obj);
52486         }, this);
52487     },
52488     
52489      /**
52490      * make a list of modules to build.
52491      * @return {Array} list of modules. 
52492      */ 
52493     
52494     buildOrder : function()
52495     {
52496         var _this = this;
52497         var cmp = function(a,b) {   
52498             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52499         };
52500         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52501             throw "No top level modules to build";
52502         }
52503         
52504         // make a flat list in order of modules to build.
52505         var mods = this.topModule ? [ this.topModule ] : [];
52506         Roo.each(this.elmodules,function(e) { mods.push(e) });
52507
52508         
52509         // add modules to their parents..
52510         var addMod = function(m) {
52511            // Roo.debug && Roo.log(m.modKey);
52512             
52513             mods.push(m);
52514             if (m.modules) {
52515                 m.modules.keySort('ASC',  cmp );
52516                 m.modules.each(addMod);
52517             }
52518             // not sure if this is used any more..
52519             if (m.finalize) {
52520                 m.finalize.name = m.name + " (clean up) ";
52521                 mods.push(m.finalize);
52522             }
52523             
52524         }
52525         if (this.topModule) { 
52526             this.topModule.modules.keySort('ASC',  cmp );
52527             this.topModule.modules.each(addMod);
52528         }
52529         return mods;
52530     },
52531     
52532      /**
52533      * Build the registered modules.
52534      * @param {Object} parent element.
52535      * @param {Function} optional method to call after module has been added.
52536      * 
52537      */ 
52538    
52539     build : function() 
52540     {
52541         
52542         this.preBuild();
52543         var mods = this.buildOrder();
52544       
52545         //this.allmods = mods;
52546         //Roo.debug && Roo.log(mods);
52547         //return;
52548         if (!mods.length) { // should not happen
52549             throw "NO modules!!!";
52550         }
52551         
52552         
52553         
52554         // flash it up as modal - so we store the mask!?
52555         Roo.MessageBox.show({ title: 'loading' });
52556         Roo.MessageBox.show({
52557            title: "Please wait...",
52558            msg: "Building Interface...",
52559            width:450,
52560            progress:true,
52561            closable:false,
52562            modal: false
52563           
52564         });
52565         var total = mods.length;
52566         
52567         var _this = this;
52568         var progressRun = function() {
52569             if (!mods.length) {
52570                 Roo.debug && Roo.log('hide?');
52571                 Roo.MessageBox.hide();
52572                 if (_this.topModule) { 
52573                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52574                 }
52575                 // THE END...
52576                 return false;   
52577             }
52578             
52579             var m = mods.shift();
52580             
52581             
52582             Roo.debug && Roo.log(m);
52583             // not sure if this is supported any more.. - modules that are are just function
52584             if (typeof(m) == 'function') { 
52585                 m.call(this);
52586                 return progressRun.defer(10, _this);
52587             } 
52588             
52589             
52590             
52591             Roo.MessageBox.updateProgress(
52592                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52593                     " of " + total + 
52594                     (m.name ? (' - ' + m.name) : '')
52595                     );
52596             
52597          
52598             // is the module disabled?
52599             var disabled = (typeof(m.disabled) == 'function') ?
52600                 m.disabled.call(m.module.disabled) : m.disabled;    
52601             
52602             
52603             if (disabled) {
52604                 return progressRun(); // we do not update the display!
52605             }
52606             
52607             // now build 
52608             
52609             m.render();
52610             // it's 10 on top level, and 1 on others??? why...
52611             return progressRun.defer(10, _this);
52612              
52613         }
52614         progressRun.defer(1, _this);
52615      
52616         
52617         
52618     }
52619     
52620      
52621    
52622     
52623     
52624 });
52625  //<script type="text/javascript">
52626
52627
52628 /**
52629  * @class Roo.Login
52630  * @extends Roo.LayoutDialog
52631  * A generic Login Dialog..... - only one needed in theory!?!?
52632  *
52633  * Fires XComponent builder on success...
52634  * 
52635  * Sends 
52636  *    username,password, lang = for login actions.
52637  *    check = 1 for periodic checking that sesion is valid.
52638  *    passwordRequest = email request password
52639  *    logout = 1 = to logout
52640  * 
52641  * Affects: (this id="????" elements)
52642  *   loading  (removed) (used to indicate application is loading)
52643  *   loading-mask (hides) (used to hide application when it's building loading)
52644  *   
52645  * 
52646  * Usage: 
52647  *    
52648  * 
52649  * Myapp.login = Roo.Login({
52650      url: xxxx,
52651    
52652      realm : 'Myapp', 
52653      
52654      
52655      method : 'POST',
52656      
52657      
52658      * 
52659  })
52660  * 
52661  * 
52662  * 
52663  **/
52664  
52665 Roo.Login = function(cfg)
52666 {
52667     this.addEvents({
52668         'refreshed' : true
52669     });
52670     
52671     Roo.apply(this,cfg);
52672     
52673     Roo.onReady(function() {
52674         this.onLoad();
52675     }, this);
52676     // call parent..
52677     
52678    
52679     Roo.Login.superclass.constructor.call(this, this);
52680     //this.addxtype(this.items[0]);
52681     
52682     
52683 }
52684
52685
52686 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52687     
52688     /**
52689      * @cfg {String} method
52690      * Method used to query for login details.
52691      */
52692     
52693     method : 'POST',
52694     /**
52695      * @cfg {String} url
52696      * URL to query login data. - eg. baseURL + '/Login.php'
52697      */
52698     url : '',
52699     
52700     /**
52701      * @property user
52702      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52703      * @type {Object} 
52704      */
52705     user : false,
52706     /**
52707      * @property checkFails
52708      * Number of times we have attempted to get authentication check, and failed.
52709      * @type {Number} 
52710      */
52711     checkFails : 0,
52712       /**
52713      * @property intervalID
52714      * The window interval that does the constant login checking.
52715      * @type {Number} 
52716      */
52717     intervalID : 0,
52718     
52719     
52720     onLoad : function() // called on page load...
52721     {
52722         // load 
52723          
52724         if (Roo.get('loading')) { // clear any loading indicator..
52725             Roo.get('loading').remove();
52726         }
52727         
52728         //this.switchLang('en'); // set the language to english..
52729        
52730         this.check({
52731             success:  function(response, opts)  {  // check successfull...
52732             
52733                 var res = this.processResponse(response);
52734                 this.checkFails =0;
52735                 if (!res.success) { // error!
52736                     this.checkFails = 5;
52737                     //console.log('call failure');
52738                     return this.failure(response,opts);
52739                 }
52740                 
52741                 if (!res.data.id) { // id=0 == login failure.
52742                     return this.show();
52743                 }
52744                 
52745                               
52746                         //console.log(success);
52747                 this.fillAuth(res.data);   
52748                 this.checkFails =0;
52749                 Roo.XComponent.build();
52750             },
52751             failure : this.show
52752         });
52753         
52754     }, 
52755     
52756     
52757     check: function(cfg) // called every so often to refresh cookie etc..
52758     {
52759         if (cfg.again) { // could be undefined..
52760             this.checkFails++;
52761         } else {
52762             this.checkFails = 0;
52763         }
52764         var _this = this;
52765         if (this.sending) {
52766             if ( this.checkFails > 4) {
52767                 Roo.MessageBox.alert("Error",  
52768                     "Error getting authentication status. - try reloading, or wait a while", function() {
52769                         _this.sending = false;
52770                     }); 
52771                 return;
52772             }
52773             cfg.again = true;
52774             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52775             return;
52776         }
52777         this.sending = true;
52778         
52779         Roo.Ajax.request({  
52780             url: this.url,
52781             params: {
52782                 getAuthUser: true
52783             },  
52784             method: this.method,
52785             success:  cfg.success || this.success,
52786             failure : cfg.failure || this.failure,
52787             scope : this,
52788             callCfg : cfg
52789               
52790         });  
52791     }, 
52792     
52793     
52794     logout: function()
52795     {
52796         window.onbeforeunload = function() { }; // false does not work for IE..
52797         this.user = false;
52798         var _this = this;
52799         
52800         Roo.Ajax.request({  
52801             url: this.url,
52802             params: {
52803                 logout: 1
52804             },  
52805             method: 'GET',
52806             failure : function() {
52807                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52808                     document.location = document.location.toString() + '?ts=' + Math.random();
52809                 });
52810                 
52811             },
52812             success : function() {
52813                 _this.user = false;
52814                 this.checkFails =0;
52815                 // fixme..
52816                 document.location = document.location.toString() + '?ts=' + Math.random();
52817             }
52818               
52819               
52820         }); 
52821     },
52822     
52823     processResponse : function (response)
52824     {
52825         var res = '';
52826         try {
52827             res = Roo.decode(response.responseText);
52828             // oops...
52829             if (typeof(res) != 'object') {
52830                 res = { success : false, errorMsg : res, errors : true };
52831             }
52832             if (typeof(res.success) == 'undefined') {
52833                 res.success = false;
52834             }
52835             
52836         } catch(e) {
52837             res = { success : false,  errorMsg : response.responseText, errors : true };
52838         }
52839         return res;
52840     },
52841     
52842     success : function(response, opts)  // check successfull...
52843     {  
52844         this.sending = false;
52845         var res = this.processResponse(response);
52846         if (!res.success) {
52847             return this.failure(response, opts);
52848         }
52849         if (!res.data || !res.data.id) {
52850             return this.failure(response,opts);
52851         }
52852         //console.log(res);
52853         this.fillAuth(res.data);
52854         
52855         this.checkFails =0;
52856         
52857     },
52858     
52859     
52860     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52861     {
52862         this.authUser = -1;
52863         this.sending = false;
52864         var res = this.processResponse(response);
52865         //console.log(res);
52866         if ( this.checkFails > 2) {
52867         
52868             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52869                 "Error getting authentication status. - try reloading"); 
52870             return;
52871         }
52872         opts.callCfg.again = true;
52873         this.check.defer(1000, this, [ opts.callCfg ]);
52874         return;  
52875     },
52876     
52877     
52878     
52879     fillAuth: function(au) {
52880         this.startAuthCheck();
52881         this.authUserId = au.id;
52882         this.authUser = au;
52883         this.lastChecked = new Date();
52884         this.fireEvent('refreshed', au);
52885         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52886         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52887         au.lang = au.lang || 'en';
52888         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52889         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52890         this.switchLang(au.lang );
52891         
52892      
52893         // open system... - -on setyp..
52894         if (this.authUserId  < 0) {
52895             Roo.MessageBox.alert("Warning", 
52896                 "This is an open system - please set up a admin user with a password.");  
52897         }
52898          
52899         //Pman.onload(); // which should do nothing if it's a re-auth result...
52900         
52901              
52902     },
52903     
52904     startAuthCheck : function() // starter for timeout checking..
52905     {
52906         if (this.intervalID) { // timer already in place...
52907             return false;
52908         }
52909         var _this = this;
52910         this.intervalID =  window.setInterval(function() {
52911               _this.check(false);
52912             }, 120000); // every 120 secs = 2mins..
52913         
52914         
52915     },
52916          
52917     
52918     switchLang : function (lang) 
52919     {
52920         _T = typeof(_T) == 'undefined' ? false : _T;
52921           if (!_T || !lang.length) {
52922             return;
52923         }
52924         
52925         if (!_T && lang != 'en') {
52926             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52927             return;
52928         }
52929         
52930         if (typeof(_T.en) == 'undefined') {
52931             _T.en = {};
52932             Roo.apply(_T.en, _T);
52933         }
52934         
52935         if (typeof(_T[lang]) == 'undefined') {
52936             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52937             return;
52938         }
52939         
52940         
52941         Roo.apply(_T, _T[lang]);
52942         // just need to set the text values for everything...
52943         var _this = this;
52944         /* this will not work ...
52945         if (this.form) { 
52946             
52947                
52948             function formLabel(name, val) {
52949                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52950             }
52951             
52952             formLabel('password', "Password"+':');
52953             formLabel('username', "Email Address"+':');
52954             formLabel('lang', "Language"+':');
52955             this.dialog.setTitle("Login");
52956             this.dialog.buttons[0].setText("Forgot Password");
52957             this.dialog.buttons[1].setText("Login");
52958         }
52959         */
52960         
52961         
52962     },
52963     
52964     
52965     title: "Login",
52966     modal: true,
52967     width:  350,
52968     //height: 230,
52969     height: 180,
52970     shadow: true,
52971     minWidth:200,
52972     minHeight:180,
52973     //proxyDrag: true,
52974     closable: false,
52975     draggable: false,
52976     collapsible: false,
52977     resizable: false,
52978     center: {  // needed??
52979         autoScroll:false,
52980         titlebar: false,
52981        // tabPosition: 'top',
52982         hideTabs: true,
52983         closeOnTab: true,
52984         alwaysShowTabs: false
52985     } ,
52986     listeners : {
52987         
52988         show  : function(dlg)
52989         {
52990             //console.log(this);
52991             this.form = this.layout.getRegion('center').activePanel.form;
52992             this.form.dialog = dlg;
52993             this.buttons[0].form = this.form;
52994             this.buttons[0].dialog = dlg;
52995             this.buttons[1].form = this.form;
52996             this.buttons[1].dialog = dlg;
52997            
52998            //this.resizeToLogo.defer(1000,this);
52999             // this is all related to resizing for logos..
53000             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53001            //// if (!sz) {
53002              //   this.resizeToLogo.defer(1000,this);
53003              //   return;
53004            // }
53005             //var w = Ext.lib.Dom.getViewWidth() - 100;
53006             //var h = Ext.lib.Dom.getViewHeight() - 100;
53007             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53008             //this.center();
53009             if (this.disabled) {
53010                 this.hide();
53011                 return;
53012             }
53013             
53014             if (this.user.id < 0) { // used for inital setup situations.
53015                 return;
53016             }
53017             
53018             if (this.intervalID) {
53019                 // remove the timer
53020                 window.clearInterval(this.intervalID);
53021                 this.intervalID = false;
53022             }
53023             
53024             
53025             if (Roo.get('loading')) {
53026                 Roo.get('loading').remove();
53027             }
53028             if (Roo.get('loading-mask')) {
53029                 Roo.get('loading-mask').hide();
53030             }
53031             
53032             //incomming._node = tnode;
53033             this.form.reset();
53034             //this.dialog.modal = !modal;
53035             //this.dialog.show();
53036             this.el.unmask(); 
53037             
53038             
53039             this.form.setValues({
53040                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53041                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53042             });
53043             
53044             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53045             if (this.form.findField('username').getValue().length > 0 ){
53046                 this.form.findField('password').focus();
53047             } else {
53048                this.form.findField('username').focus();
53049             }
53050     
53051         }
53052     },
53053     items : [
53054          {
53055        
53056             xtype : 'ContentPanel',
53057             xns : Roo,
53058             region: 'center',
53059             fitToFrame : true,
53060             
53061             items : [
53062     
53063                 {
53064                
53065                     xtype : 'Form',
53066                     xns : Roo.form,
53067                     labelWidth: 100,
53068                     style : 'margin: 10px;',
53069                     
53070                     listeners : {
53071                         actionfailed : function(f, act) {
53072                             // form can return { errors: .... }
53073                                 
53074                             //act.result.errors // invalid form element list...
53075                             //act.result.errorMsg// invalid form element list...
53076                             
53077                             this.dialog.el.unmask();
53078                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53079                                         "Login failed - communication error - try again.");
53080                                       
53081                         },
53082                         actioncomplete: function(re, act) {
53083                              
53084                             Roo.state.Manager.set(
53085                                 this.dialog.realm + '.username',  
53086                                     this.findField('username').getValue()
53087                             );
53088                             Roo.state.Manager.set(
53089                                 this.dialog.realm + '.lang',  
53090                                 this.findField('lang').getValue() 
53091                             );
53092                             
53093                             this.dialog.fillAuth(act.result.data);
53094                               
53095                             this.dialog.hide();
53096                             
53097                             if (Roo.get('loading-mask')) {
53098                                 Roo.get('loading-mask').show();
53099                             }
53100                             Roo.XComponent.build();
53101                             
53102                              
53103                             
53104                         }
53105                     },
53106                     items : [
53107                         {
53108                             xtype : 'TextField',
53109                             xns : Roo.form,
53110                             fieldLabel: "Email Address",
53111                             name: 'username',
53112                             width:200,
53113                             autoCreate : {tag: "input", type: "text", size: "20"}
53114                         },
53115                         {
53116                             xtype : 'TextField',
53117                             xns : Roo.form,
53118                             fieldLabel: "Password",
53119                             inputType: 'password',
53120                             name: 'password',
53121                             width:200,
53122                             autoCreate : {tag: "input", type: "text", size: "20"},
53123                             listeners : {
53124                                 specialkey : function(e,ev) {
53125                                     if (ev.keyCode == 13) {
53126                                         this.form.dialog.el.mask("Logging in");
53127                                         this.form.doAction('submit', {
53128                                             url: this.form.dialog.url,
53129                                             method: this.form.dialog.method
53130                                         });
53131                                     }
53132                                 }
53133                             }  
53134                         },
53135                         {
53136                             xtype : 'ComboBox',
53137                             xns : Roo.form,
53138                             fieldLabel: "Language",
53139                             name : 'langdisp',
53140                             store: {
53141                                 xtype : 'SimpleStore',
53142                                 fields: ['lang', 'ldisp'],
53143                                 data : [
53144                                     [ 'en', 'English' ],
53145                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53146                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53147                                 ]
53148                             },
53149                             
53150                             valueField : 'lang',
53151                             hiddenName:  'lang',
53152                             width: 200,
53153                             displayField:'ldisp',
53154                             typeAhead: false,
53155                             editable: false,
53156                             mode: 'local',
53157                             triggerAction: 'all',
53158                             emptyText:'Select a Language...',
53159                             selectOnFocus:true,
53160                             listeners : {
53161                                 select :  function(cb, rec, ix) {
53162                                     this.form.switchLang(rec.data.lang);
53163                                 }
53164                             }
53165                         
53166                         }
53167                     ]
53168                 }
53169                   
53170                 
53171             ]
53172         }
53173     ],
53174     buttons : [
53175         {
53176             xtype : 'Button',
53177             xns : 'Roo',
53178             text : "Forgot Password",
53179             listeners : {
53180                 click : function() {
53181                     //console.log(this);
53182                     var n = this.form.findField('username').getValue();
53183                     if (!n.length) {
53184                         Roo.MessageBox.alert("Error", "Fill in your email address");
53185                         return;
53186                     }
53187                     Roo.Ajax.request({
53188                         url: this.dialog.url,
53189                         params: {
53190                             passwordRequest: n
53191                         },
53192                         method: this.dialog.method,
53193                         success:  function(response, opts)  {  // check successfull...
53194                         
53195                             var res = this.dialog.processResponse(response);
53196                             if (!res.success) { // error!
53197                                Roo.MessageBox.alert("Error" ,
53198                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53199                                return;
53200                             }
53201                             Roo.MessageBox.alert("Notice" ,
53202                                 "Please check you email for the Password Reset message");
53203                         },
53204                         failure : function() {
53205                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53206                         }
53207                         
53208                     });
53209                 }
53210             }
53211         },
53212         {
53213             xtype : 'Button',
53214             xns : 'Roo',
53215             text : "Login",
53216             listeners : {
53217                 
53218                 click : function () {
53219                         
53220                     this.dialog.el.mask("Logging in");
53221                     this.form.doAction('submit', {
53222                             url: this.dialog.url,
53223                             method: this.dialog.method
53224                     });
53225                 }
53226             }
53227         }
53228     ]
53229   
53230   
53231 })
53232  
53233
53234
53235