roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11916      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19557             return;
19558         }
19559         var r = o.records, t = o.totalRecords || r.length;
19560         if(!options || options.add !== true){
19561             if(this.pruneModifiedRecords){
19562                 this.modified = [];
19563             }
19564             for(var i = 0, len = r.length; i < len; i++){
19565                 r[i].join(this);
19566             }
19567             if(this.snapshot){
19568                 this.data = this.snapshot;
19569                 delete this.snapshot;
19570             }
19571             this.data.clear();
19572             this.data.addAll(r);
19573             this.totalLength = t;
19574             this.applySort();
19575             this.fireEvent("datachanged", this);
19576         }else{
19577             this.totalLength = Math.max(t, this.data.length+r.length);
19578             this.add(r);
19579         }
19580         this.fireEvent("load", this, r, options);
19581         if(options.callback){
19582             options.callback.call(options.scope || this, r, options, true);
19583         }
19584     },
19585
19586     /**
19587      * Loads data from a passed data block. A Reader which understands the format of the data
19588      * must have been configured in the constructor.
19589      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19590      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19591      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19592      */
19593     loadData : function(o, append){
19594         var r = this.reader.readRecords(o);
19595         this.loadRecords(r, {add: append}, true);
19596     },
19597
19598     /**
19599      * Gets the number of cached records.
19600      * <p>
19601      * <em>If using paging, this may not be the total size of the dataset. If the data object
19602      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19603      * the data set size</em>
19604      */
19605     getCount : function(){
19606         return this.data.length || 0;
19607     },
19608
19609     /**
19610      * Gets the total number of records in the dataset as returned by the server.
19611      * <p>
19612      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19613      * the dataset size</em>
19614      */
19615     getTotalCount : function(){
19616         return this.totalLength || 0;
19617     },
19618
19619     /**
19620      * Returns the sort state of the Store as an object with two properties:
19621      * <pre><code>
19622  field {String} The name of the field by which the Records are sorted
19623  direction {String} The sort order, "ASC" or "DESC"
19624      * </code></pre>
19625      */
19626     getSortState : function(){
19627         return this.sortInfo;
19628     },
19629
19630     // private
19631     applySort : function(){
19632         if(this.sortInfo && !this.remoteSort){
19633             var s = this.sortInfo, f = s.field;
19634             var st = this.fields.get(f).sortType;
19635             var fn = function(r1, r2){
19636                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19637                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19638             };
19639             this.data.sort(s.direction, fn);
19640             if(this.snapshot && this.snapshot != this.data){
19641                 this.snapshot.sort(s.direction, fn);
19642             }
19643         }
19644     },
19645
19646     /**
19647      * Sets the default sort column and order to be used by the next load operation.
19648      * @param {String} fieldName The name of the field to sort by.
19649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19650      */
19651     setDefaultSort : function(field, dir){
19652         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19653     },
19654
19655     /**
19656      * Sort the Records.
19657      * If remote sorting is used, the sort is performed on the server, and the cache is
19658      * reloaded. If local sorting is used, the cache is sorted internally.
19659      * @param {String} fieldName The name of the field to sort by.
19660      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19661      */
19662     sort : function(fieldName, dir){
19663         var f = this.fields.get(fieldName);
19664         if(!dir){
19665             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19666             
19667             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19668                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19669             }else{
19670                 dir = f.sortDir;
19671             }
19672         }
19673         this.sortToggle[f.name] = dir;
19674         this.sortInfo = {field: f.name, direction: dir};
19675         if(!this.remoteSort){
19676             this.applySort();
19677             this.fireEvent("datachanged", this);
19678         }else{
19679             this.load(this.lastOptions);
19680         }
19681     },
19682
19683     /**
19684      * Calls the specified function for each of the Records in the cache.
19685      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19686      * Returning <em>false</em> aborts and exits the iteration.
19687      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19688      */
19689     each : function(fn, scope){
19690         this.data.each(fn, scope);
19691     },
19692
19693     /**
19694      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19695      * (e.g., during paging).
19696      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19697      */
19698     getModifiedRecords : function(){
19699         return this.modified;
19700     },
19701
19702     // private
19703     createFilterFn : function(property, value, anyMatch){
19704         if(!value.exec){ // not a regex
19705             value = String(value);
19706             if(value.length == 0){
19707                 return false;
19708             }
19709             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19710         }
19711         return function(r){
19712             return value.test(r.data[property]);
19713         };
19714     },
19715
19716     /**
19717      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19718      * @param {String} property A field on your records
19719      * @param {Number} start The record index to start at (defaults to 0)
19720      * @param {Number} end The last record index to include (defaults to length - 1)
19721      * @return {Number} The sum
19722      */
19723     sum : function(property, start, end){
19724         var rs = this.data.items, v = 0;
19725         start = start || 0;
19726         end = (end || end === 0) ? end : rs.length-1;
19727
19728         for(var i = start; i <= end; i++){
19729             v += (rs[i].data[property] || 0);
19730         }
19731         return v;
19732     },
19733
19734     /**
19735      * Filter the records by a specified property.
19736      * @param {String} field A field on your records
19737      * @param {String/RegExp} value Either a string that the field
19738      * should start with or a RegExp to test against the field
19739      * @param {Boolean} anyMatch True to match any part not just the beginning
19740      */
19741     filter : function(property, value, anyMatch){
19742         var fn = this.createFilterFn(property, value, anyMatch);
19743         return fn ? this.filterBy(fn) : this.clearFilter();
19744     },
19745
19746     /**
19747      * Filter by a function. The specified function will be called with each
19748      * record in this data source. If the function returns true the record is included,
19749      * otherwise it is filtered.
19750      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19751      * @param {Object} scope (optional) The scope of the function (defaults to this)
19752      */
19753     filterBy : function(fn, scope){
19754         this.snapshot = this.snapshot || this.data;
19755         this.data = this.queryBy(fn, scope||this);
19756         this.fireEvent("datachanged", this);
19757     },
19758
19759     /**
19760      * Query the records by a specified property.
19761      * @param {String} field A field on your records
19762      * @param {String/RegExp} value Either a string that the field
19763      * should start with or a RegExp to test against the field
19764      * @param {Boolean} anyMatch True to match any part not just the beginning
19765      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19766      */
19767     query : function(property, value, anyMatch){
19768         var fn = this.createFilterFn(property, value, anyMatch);
19769         return fn ? this.queryBy(fn) : this.data.clone();
19770     },
19771
19772     /**
19773      * Query by a function. The specified function will be called with each
19774      * record in this data source. If the function returns true the record is included
19775      * in the results.
19776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19777      * @param {Object} scope (optional) The scope of the function (defaults to this)
19778       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19779      **/
19780     queryBy : function(fn, scope){
19781         var data = this.snapshot || this.data;
19782         return data.filterBy(fn, scope||this);
19783     },
19784
19785     /**
19786      * Collects unique values for a particular dataIndex from this store.
19787      * @param {String} dataIndex The property to collect
19788      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19789      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19790      * @return {Array} An array of the unique values
19791      **/
19792     collect : function(dataIndex, allowNull, bypassFilter){
19793         var d = (bypassFilter === true && this.snapshot) ?
19794                 this.snapshot.items : this.data.items;
19795         var v, sv, r = [], l = {};
19796         for(var i = 0, len = d.length; i < len; i++){
19797             v = d[i].data[dataIndex];
19798             sv = String(v);
19799             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19800                 l[sv] = true;
19801                 r[r.length] = v;
19802             }
19803         }
19804         return r;
19805     },
19806
19807     /**
19808      * Revert to a view of the Record cache with no filtering applied.
19809      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19810      */
19811     clearFilter : function(suppressEvent){
19812         if(this.snapshot && this.snapshot != this.data){
19813             this.data = this.snapshot;
19814             delete this.snapshot;
19815             if(suppressEvent !== true){
19816                 this.fireEvent("datachanged", this);
19817             }
19818         }
19819     },
19820
19821     // private
19822     afterEdit : function(record){
19823         if(this.modified.indexOf(record) == -1){
19824             this.modified.push(record);
19825         }
19826         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19827     },
19828
19829     // private
19830     afterReject : function(record){
19831         this.modified.remove(record);
19832         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19833     },
19834
19835     // private
19836     afterCommit : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19839     },
19840
19841     /**
19842      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19843      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19844      */
19845     commitChanges : function(){
19846         var m = this.modified.slice(0);
19847         this.modified = [];
19848         for(var i = 0, len = m.length; i < len; i++){
19849             m[i].commit();
19850         }
19851     },
19852
19853     /**
19854      * Cancel outstanding changes on all changed records.
19855      */
19856     rejectChanges : function(){
19857         var m = this.modified.slice(0);
19858         this.modified = [];
19859         for(var i = 0, len = m.length; i < len; i++){
19860             m[i].reject();
19861         }
19862     },
19863
19864     onMetaChange : function(meta, rtype, o){
19865         this.recordType = rtype;
19866         this.fields = rtype.prototype.fields;
19867         delete this.snapshot;
19868         this.sortInfo = meta.sortInfo || this.sortInfo;
19869         this.modified = [];
19870         this.fireEvent('metachange', this, this.reader.meta);
19871     }
19872 });/*
19873  * Based on:
19874  * Ext JS Library 1.1.1
19875  * Copyright(c) 2006-2007, Ext JS, LLC.
19876  *
19877  * Originally Released Under LGPL - original licence link has changed is not relivant.
19878  *
19879  * Fork - LGPL
19880  * <script type="text/javascript">
19881  */
19882
19883 /**
19884  * @class Roo.data.SimpleStore
19885  * @extends Roo.data.Store
19886  * Small helper class to make creating Stores from Array data easier.
19887  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19888  * @cfg {Array} fields An array of field definition objects, or field name strings.
19889  * @cfg {Array} data The multi-dimensional array of data
19890  * @constructor
19891  * @param {Object} config
19892  */
19893 Roo.data.SimpleStore = function(config){
19894     Roo.data.SimpleStore.superclass.constructor.call(this, {
19895         isLocal : true,
19896         reader: new Roo.data.ArrayReader({
19897                 id: config.id
19898             },
19899             Roo.data.Record.create(config.fields)
19900         ),
19901         proxy : new Roo.data.MemoryProxy(config.data)
19902     });
19903     this.load();
19904 };
19905 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19906  * Based on:
19907  * Ext JS Library 1.1.1
19908  * Copyright(c) 2006-2007, Ext JS, LLC.
19909  *
19910  * Originally Released Under LGPL - original licence link has changed is not relivant.
19911  *
19912  * Fork - LGPL
19913  * <script type="text/javascript">
19914  */
19915
19916 /**
19917 /**
19918  * @extends Roo.data.Store
19919  * @class Roo.data.JsonStore
19920  * Small helper class to make creating Stores for JSON data easier. <br/>
19921 <pre><code>
19922 var store = new Roo.data.JsonStore({
19923     url: 'get-images.php',
19924     root: 'images',
19925     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19926 });
19927 </code></pre>
19928  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19929  * JsonReader and HttpProxy (unless inline data is provided).</b>
19930  * @cfg {Array} fields An array of field definition objects, or field name strings.
19931  * @constructor
19932  * @param {Object} config
19933  */
19934 Roo.data.JsonStore = function(c){
19935     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19936         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19937         reader: new Roo.data.JsonReader(c, c.fields)
19938     }));
19939 };
19940 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19941  * Based on:
19942  * Ext JS Library 1.1.1
19943  * Copyright(c) 2006-2007, Ext JS, LLC.
19944  *
19945  * Originally Released Under LGPL - original licence link has changed is not relivant.
19946  *
19947  * Fork - LGPL
19948  * <script type="text/javascript">
19949  */
19950
19951  
19952 Roo.data.Field = function(config){
19953     if(typeof config == "string"){
19954         config = {name: config};
19955     }
19956     Roo.apply(this, config);
19957     
19958     if(!this.type){
19959         this.type = "auto";
19960     }
19961     
19962     var st = Roo.data.SortTypes;
19963     // named sortTypes are supported, here we look them up
19964     if(typeof this.sortType == "string"){
19965         this.sortType = st[this.sortType];
19966     }
19967     
19968     // set default sortType for strings and dates
19969     if(!this.sortType){
19970         switch(this.type){
19971             case "string":
19972                 this.sortType = st.asUCString;
19973                 break;
19974             case "date":
19975                 this.sortType = st.asDate;
19976                 break;
19977             default:
19978                 this.sortType = st.none;
19979         }
19980     }
19981
19982     // define once
19983     var stripRe = /[\$,%]/g;
19984
19985     // prebuilt conversion function for this field, instead of
19986     // switching every time we're reading a value
19987     if(!this.convert){
19988         var cv, dateFormat = this.dateFormat;
19989         switch(this.type){
19990             case "":
19991             case "auto":
19992             case undefined:
19993                 cv = function(v){ return v; };
19994                 break;
19995             case "string":
19996                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19997                 break;
19998             case "int":
19999                 cv = function(v){
20000                     return v !== undefined && v !== null && v !== '' ?
20001                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20002                     };
20003                 break;
20004             case "float":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20008                     };
20009                 break;
20010             case "bool":
20011             case "boolean":
20012                 cv = function(v){ return v === true || v === "true" || v == 1; };
20013                 break;
20014             case "date":
20015                 cv = function(v){
20016                     if(!v){
20017                         return '';
20018                     }
20019                     if(v instanceof Date){
20020                         return v;
20021                     }
20022                     if(dateFormat){
20023                         if(dateFormat == "timestamp"){
20024                             return new Date(v*1000);
20025                         }
20026                         return Date.parseDate(v, dateFormat);
20027                     }
20028                     var parsed = Date.parse(v);
20029                     return parsed ? new Date(parsed) : null;
20030                 };
20031              break;
20032             
20033         }
20034         this.convert = cv;
20035     }
20036 };
20037
20038 Roo.data.Field.prototype = {
20039     dateFormat: null,
20040     defaultValue: "",
20041     mapping: null,
20042     sortType : null,
20043     sortDir : "ASC"
20044 };/*
20045  * Based on:
20046  * Ext JS Library 1.1.1
20047  * Copyright(c) 2006-2007, Ext JS, LLC.
20048  *
20049  * Originally Released Under LGPL - original licence link has changed is not relivant.
20050  *
20051  * Fork - LGPL
20052  * <script type="text/javascript">
20053  */
20054  
20055 // Base class for reading structured data from a data source.  This class is intended to be
20056 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20057
20058 /**
20059  * @class Roo.data.DataReader
20060  * Base class for reading structured data from a data source.  This class is intended to be
20061  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20062  */
20063
20064 Roo.data.DataReader = function(meta, recordType){
20065     
20066     this.meta = meta;
20067     
20068     this.recordType = recordType instanceof Array ? 
20069         Roo.data.Record.create(recordType) : recordType;
20070 };
20071
20072 Roo.data.DataReader.prototype = {
20073      /**
20074      * Create an empty record
20075      * @param {Object} data (optional) - overlay some values
20076      * @return {Roo.data.Record} record created.
20077      */
20078     newRow :  function(d) {
20079         var da =  {};
20080         this.recordType.prototype.fields.each(function(c) {
20081             switch( c.type) {
20082                 case 'int' : da[c.name] = 0; break;
20083                 case 'date' : da[c.name] = new Date(); break;
20084                 case 'float' : da[c.name] = 0.0; break;
20085                 case 'boolean' : da[c.name] = false; break;
20086                 default : da[c.name] = ""; break;
20087             }
20088             
20089         });
20090         return new this.recordType(Roo.apply(da, d));
20091     }
20092     
20093 };/*
20094  * Based on:
20095  * Ext JS Library 1.1.1
20096  * Copyright(c) 2006-2007, Ext JS, LLC.
20097  *
20098  * Originally Released Under LGPL - original licence link has changed is not relivant.
20099  *
20100  * Fork - LGPL
20101  * <script type="text/javascript">
20102  */
20103
20104 /**
20105  * @class Roo.data.DataProxy
20106  * @extends Roo.data.Observable
20107  * This class is an abstract base class for implementations which provide retrieval of
20108  * unformatted data objects.<br>
20109  * <p>
20110  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20111  * (of the appropriate type which knows how to parse the data object) to provide a block of
20112  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20113  * <p>
20114  * Custom implementations must implement the load method as described in
20115  * {@link Roo.data.HttpProxy#load}.
20116  */
20117 Roo.data.DataProxy = function(){
20118     this.addEvents({
20119         /**
20120          * @event beforeload
20121          * Fires before a network request is made to retrieve a data object.
20122          * @param {Object} This DataProxy object.
20123          * @param {Object} params The params parameter to the load function.
20124          */
20125         beforeload : true,
20126         /**
20127          * @event load
20128          * Fires before the load method's callback is called.
20129          * @param {Object} This DataProxy object.
20130          * @param {Object} o The data object.
20131          * @param {Object} arg The callback argument object passed to the load function.
20132          */
20133         load : true,
20134         /**
20135          * @event loadexception
20136          * Fires if an Exception occurs during data retrieval.
20137          * @param {Object} This DataProxy object.
20138          * @param {Object} o The data object.
20139          * @param {Object} arg The callback argument object passed to the load function.
20140          * @param {Object} e The Exception.
20141          */
20142         loadexception : true
20143     });
20144     Roo.data.DataProxy.superclass.constructor.call(this);
20145 };
20146
20147 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20148
20149     /**
20150      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20151      */
20152 /*
20153  * Based on:
20154  * Ext JS Library 1.1.1
20155  * Copyright(c) 2006-2007, Ext JS, LLC.
20156  *
20157  * Originally Released Under LGPL - original licence link has changed is not relivant.
20158  *
20159  * Fork - LGPL
20160  * <script type="text/javascript">
20161  */
20162 /**
20163  * @class Roo.data.MemoryProxy
20164  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20165  * to the Reader when its load method is called.
20166  * @constructor
20167  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20168  */
20169 Roo.data.MemoryProxy = function(data){
20170     if (data.data) {
20171         data = data.data;
20172     }
20173     Roo.data.MemoryProxy.superclass.constructor.call(this);
20174     this.data = data;
20175 };
20176
20177 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20178     /**
20179      * Load data from the requested source (in this case an in-memory
20180      * data object passed to the constructor), read the data object into
20181      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20182      * process that block using the passed callback.
20183      * @param {Object} params This parameter is not used by the MemoryProxy class.
20184      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20185      * object into a block of Roo.data.Records.
20186      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20187      * The function must be passed <ul>
20188      * <li>The Record block object</li>
20189      * <li>The "arg" argument from the load function</li>
20190      * <li>A boolean success indicator</li>
20191      * </ul>
20192      * @param {Object} scope The scope in which to call the callback
20193      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20194      */
20195     load : function(params, reader, callback, scope, arg){
20196         params = params || {};
20197         var result;
20198         try {
20199             result = reader.readRecords(this.data);
20200         }catch(e){
20201             this.fireEvent("loadexception", this, arg, null, e);
20202             callback.call(scope, null, arg, false);
20203             return;
20204         }
20205         callback.call(scope, result, arg, true);
20206     },
20207     
20208     // private
20209     update : function(params, records){
20210         
20211     }
20212 });/*
20213  * Based on:
20214  * Ext JS Library 1.1.1
20215  * Copyright(c) 2006-2007, Ext JS, LLC.
20216  *
20217  * Originally Released Under LGPL - original licence link has changed is not relivant.
20218  *
20219  * Fork - LGPL
20220  * <script type="text/javascript">
20221  */
20222 /**
20223  * @class Roo.data.HttpProxy
20224  * @extends Roo.data.DataProxy
20225  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20226  * configured to reference a certain URL.<br><br>
20227  * <p>
20228  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20229  * from which the running page was served.<br><br>
20230  * <p>
20231  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20232  * <p>
20233  * Be aware that to enable the browser to parse an XML document, the server must set
20234  * the Content-Type header in the HTTP response to "text/xml".
20235  * @constructor
20236  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20237  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20238  * will be used to make the request.
20239  */
20240 Roo.data.HttpProxy = function(conn){
20241     Roo.data.HttpProxy.superclass.constructor.call(this);
20242     // is conn a conn config or a real conn?
20243     this.conn = conn;
20244     this.useAjax = !conn || !conn.events;
20245   
20246 };
20247
20248 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20249     // thse are take from connection...
20250     
20251     /**
20252      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20253      */
20254     /**
20255      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20256      * extra parameters to each request made by this object. (defaults to undefined)
20257      */
20258     /**
20259      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20260      *  to each request made by this object. (defaults to undefined)
20261      */
20262     /**
20263      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20264      */
20265     /**
20266      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20267      */
20268      /**
20269      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20270      * @type Boolean
20271      */
20272   
20273
20274     /**
20275      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20276      * @type Boolean
20277      */
20278     /**
20279      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20280      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20281      * a finer-grained basis than the DataProxy events.
20282      */
20283     getConnection : function(){
20284         return this.useAjax ? Roo.Ajax : this.conn;
20285     },
20286
20287     /**
20288      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20289      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20290      * process that block using the passed callback.
20291      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20292      * for the request to the remote server.
20293      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20294      * object into a block of Roo.data.Records.
20295      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20296      * The function must be passed <ul>
20297      * <li>The Record block object</li>
20298      * <li>The "arg" argument from the load function</li>
20299      * <li>A boolean success indicator</li>
20300      * </ul>
20301      * @param {Object} scope The scope in which to call the callback
20302      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20303      */
20304     load : function(params, reader, callback, scope, arg){
20305         if(this.fireEvent("beforeload", this, params) !== false){
20306             var  o = {
20307                 params : params || {},
20308                 request: {
20309                     callback : callback,
20310                     scope : scope,
20311                     arg : arg
20312                 },
20313                 reader: reader,
20314                 callback : this.loadResponse,
20315                 scope: this
20316             };
20317             if(this.useAjax){
20318                 Roo.applyIf(o, this.conn);
20319                 if(this.activeRequest){
20320                     Roo.Ajax.abort(this.activeRequest);
20321                 }
20322                 this.activeRequest = Roo.Ajax.request(o);
20323             }else{
20324                 this.conn.request(o);
20325             }
20326         }else{
20327             callback.call(scope||this, null, arg, false);
20328         }
20329     },
20330
20331     // private
20332     loadResponse : function(o, success, response){
20333         delete this.activeRequest;
20334         if(!success){
20335             this.fireEvent("loadexception", this, o, response);
20336             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20337             return;
20338         }
20339         var result;
20340         try {
20341             result = o.reader.read(response);
20342         }catch(e){
20343             this.fireEvent("loadexception", this, o, response, e);
20344             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20345             return;
20346         }
20347         
20348         this.fireEvent("load", this, o, o.request.arg);
20349         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20350     },
20351
20352     // private
20353     update : function(dataSet){
20354
20355     },
20356
20357     // private
20358     updateResponse : function(dataSet){
20359
20360     }
20361 });/*
20362  * Based on:
20363  * Ext JS Library 1.1.1
20364  * Copyright(c) 2006-2007, Ext JS, LLC.
20365  *
20366  * Originally Released Under LGPL - original licence link has changed is not relivant.
20367  *
20368  * Fork - LGPL
20369  * <script type="text/javascript">
20370  */
20371
20372 /**
20373  * @class Roo.data.ScriptTagProxy
20374  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20375  * other than the originating domain of the running page.<br><br>
20376  * <p>
20377  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20378  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20379  * <p>
20380  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20381  * source code that is used as the source inside a &lt;script> tag.<br><br>
20382  * <p>
20383  * In order for the browser to process the returned data, the server must wrap the data object
20384  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20385  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20386  * depending on whether the callback name was passed:
20387  * <p>
20388  * <pre><code>
20389 boolean scriptTag = false;
20390 String cb = request.getParameter("callback");
20391 if (cb != null) {
20392     scriptTag = true;
20393     response.setContentType("text/javascript");
20394 } else {
20395     response.setContentType("application/x-json");
20396 }
20397 Writer out = response.getWriter();
20398 if (scriptTag) {
20399     out.write(cb + "(");
20400 }
20401 out.print(dataBlock.toJsonString());
20402 if (scriptTag) {
20403     out.write(");");
20404 }
20405 </pre></code>
20406  *
20407  * @constructor
20408  * @param {Object} config A configuration object.
20409  */
20410 Roo.data.ScriptTagProxy = function(config){
20411     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20412     Roo.apply(this, config);
20413     this.head = document.getElementsByTagName("head")[0];
20414 };
20415
20416 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20417
20418 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20419     /**
20420      * @cfg {String} url The URL from which to request the data object.
20421      */
20422     /**
20423      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20424      */
20425     timeout : 30000,
20426     /**
20427      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20428      * the server the name of the callback function set up by the load call to process the returned data object.
20429      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20430      * javascript output which calls this named function passing the data object as its only parameter.
20431      */
20432     callbackParam : "callback",
20433     /**
20434      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20435      * name to the request.
20436      */
20437     nocache : true,
20438
20439     /**
20440      * Load data from the configured URL, read the data object into
20441      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20442      * process that block using the passed callback.
20443      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20444      * for the request to the remote server.
20445      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20446      * object into a block of Roo.data.Records.
20447      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20448      * The function must be passed <ul>
20449      * <li>The Record block object</li>
20450      * <li>The "arg" argument from the load function</li>
20451      * <li>A boolean success indicator</li>
20452      * </ul>
20453      * @param {Object} scope The scope in which to call the callback
20454      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20455      */
20456     load : function(params, reader, callback, scope, arg){
20457         if(this.fireEvent("beforeload", this, params) !== false){
20458
20459             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20460
20461             var url = this.url;
20462             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20463             if(this.nocache){
20464                 url += "&_dc=" + (new Date().getTime());
20465             }
20466             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20467             var trans = {
20468                 id : transId,
20469                 cb : "stcCallback"+transId,
20470                 scriptId : "stcScript"+transId,
20471                 params : params,
20472                 arg : arg,
20473                 url : url,
20474                 callback : callback,
20475                 scope : scope,
20476                 reader : reader
20477             };
20478             var conn = this;
20479
20480             window[trans.cb] = function(o){
20481                 conn.handleResponse(o, trans);
20482             };
20483
20484             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20485
20486             if(this.autoAbort !== false){
20487                 this.abort();
20488             }
20489
20490             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20491
20492             var script = document.createElement("script");
20493             script.setAttribute("src", url);
20494             script.setAttribute("type", "text/javascript");
20495             script.setAttribute("id", trans.scriptId);
20496             this.head.appendChild(script);
20497
20498             this.trans = trans;
20499         }else{
20500             callback.call(scope||this, null, arg, false);
20501         }
20502     },
20503
20504     // private
20505     isLoading : function(){
20506         return this.trans ? true : false;
20507     },
20508
20509     /**
20510      * Abort the current server request.
20511      */
20512     abort : function(){
20513         if(this.isLoading()){
20514             this.destroyTrans(this.trans);
20515         }
20516     },
20517
20518     // private
20519     destroyTrans : function(trans, isLoaded){
20520         this.head.removeChild(document.getElementById(trans.scriptId));
20521         clearTimeout(trans.timeoutId);
20522         if(isLoaded){
20523             window[trans.cb] = undefined;
20524             try{
20525                 delete window[trans.cb];
20526             }catch(e){}
20527         }else{
20528             // if hasn't been loaded, wait for load to remove it to prevent script error
20529             window[trans.cb] = function(){
20530                 window[trans.cb] = undefined;
20531                 try{
20532                     delete window[trans.cb];
20533                 }catch(e){}
20534             };
20535         }
20536     },
20537
20538     // private
20539     handleResponse : function(o, trans){
20540         this.trans = false;
20541         this.destroyTrans(trans, true);
20542         var result;
20543         try {
20544             result = trans.reader.readRecords(o);
20545         }catch(e){
20546             this.fireEvent("loadexception", this, o, trans.arg, e);
20547             trans.callback.call(trans.scope||window, null, trans.arg, false);
20548             return;
20549         }
20550         this.fireEvent("load", this, o, trans.arg);
20551         trans.callback.call(trans.scope||window, result, trans.arg, true);
20552     },
20553
20554     // private
20555     handleFailure : function(trans){
20556         this.trans = false;
20557         this.destroyTrans(trans, false);
20558         this.fireEvent("loadexception", this, null, trans.arg);
20559         trans.callback.call(trans.scope||window, null, trans.arg, false);
20560     }
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571
20572 /**
20573  * @class Roo.data.JsonReader
20574  * @extends Roo.data.DataReader
20575  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20576  * based on mappings in a provided Roo.data.Record constructor.
20577  * 
20578  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20579  * in the reply previously. 
20580  * 
20581  * <p>
20582  * Example code:
20583  * <pre><code>
20584 var RecordDef = Roo.data.Record.create([
20585     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20586     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20587 ]);
20588 var myReader = new Roo.data.JsonReader({
20589     totalProperty: "results",    // The property which contains the total dataset size (optional)
20590     root: "rows",                // The property which contains an Array of row objects
20591     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20592 }, RecordDef);
20593 </code></pre>
20594  * <p>
20595  * This would consume a JSON file like this:
20596  * <pre><code>
20597 { 'results': 2, 'rows': [
20598     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20599     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20600 }
20601 </code></pre>
20602  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20603  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20604  * paged from the remote server.
20605  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20606  * @cfg {String} root name of the property which contains the Array of row objects.
20607  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20608  * @constructor
20609  * Create a new JsonReader
20610  * @param {Object} meta Metadata configuration options
20611  * @param {Object} recordType Either an Array of field definition objects,
20612  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20613  */
20614 Roo.data.JsonReader = function(meta, recordType){
20615     
20616     meta = meta || {};
20617     // set some defaults:
20618     Roo.applyIf(meta, {
20619         totalProperty: 'total',
20620         successProperty : 'success',
20621         root : 'data',
20622         id : 'id'
20623     });
20624     
20625     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20626 };
20627 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20628     
20629     /**
20630      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20631      * Used by Store query builder to append _requestMeta to params.
20632      * 
20633      */
20634     metaFromRemote : false,
20635     /**
20636      * This method is only used by a DataProxy which has retrieved data from a remote server.
20637      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20638      * @return {Object} data A data block which is used by an Roo.data.Store object as
20639      * a cache of Roo.data.Records.
20640      */
20641     read : function(response){
20642         var json = response.responseText;
20643        
20644         var o = /* eval:var:o */ eval("("+json+")");
20645         if(!o) {
20646             throw {message: "JsonReader.read: Json object not found"};
20647         }
20648         
20649         if(o.metaData){
20650             
20651             delete this.ef;
20652             this.metaFromRemote = true;
20653             this.meta = o.metaData;
20654             this.recordType = Roo.data.Record.create(o.metaData.fields);
20655             this.onMetaChange(this.meta, this.recordType, o);
20656         }
20657         return this.readRecords(o);
20658     },
20659
20660     // private function a store will implement
20661     onMetaChange : function(meta, recordType, o){
20662
20663     },
20664
20665     /**
20666          * @ignore
20667          */
20668     simpleAccess: function(obj, subsc) {
20669         return obj[subsc];
20670     },
20671
20672         /**
20673          * @ignore
20674          */
20675     getJsonAccessor: function(){
20676         var re = /[\[\.]/;
20677         return function(expr) {
20678             try {
20679                 return(re.test(expr))
20680                     ? new Function("obj", "return obj." + expr)
20681                     : function(obj){
20682                         return obj[expr];
20683                     };
20684             } catch(e){}
20685             return Roo.emptyFn;
20686         };
20687     }(),
20688
20689     /**
20690      * Create a data block containing Roo.data.Records from an XML document.
20691      * @param {Object} o An object which contains an Array of row objects in the property specified
20692      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20693      * which contains the total size of the dataset.
20694      * @return {Object} data A data block which is used by an Roo.data.Store object as
20695      * a cache of Roo.data.Records.
20696      */
20697     readRecords : function(o){
20698         /**
20699          * After any data loads, the raw JSON data is available for further custom processing.
20700          * @type Object
20701          */
20702         this.jsonData = o;
20703         var s = this.meta, Record = this.recordType,
20704             f = Record.prototype.fields, fi = f.items, fl = f.length;
20705
20706 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20707         if (!this.ef) {
20708             if(s.totalProperty) {
20709                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20710                 }
20711                 if(s.successProperty) {
20712                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20713                 }
20714                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20715                 if (s.id) {
20716                         var g = this.getJsonAccessor(s.id);
20717                         this.getId = function(rec) {
20718                                 var r = g(rec);
20719                                 return (r === undefined || r === "") ? null : r;
20720                         };
20721                 } else {
20722                         this.getId = function(){return null;};
20723                 }
20724             this.ef = [];
20725             for(var jj = 0; jj < fl; jj++){
20726                 f = fi[jj];
20727                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20728                 this.ef[jj] = this.getJsonAccessor(map);
20729             }
20730         }
20731
20732         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20733         if(s.totalProperty){
20734             var vt = parseInt(this.getTotal(o), 10);
20735             if(!isNaN(vt)){
20736                 totalRecords = vt;
20737             }
20738         }
20739         if(s.successProperty){
20740             var vs = this.getSuccess(o);
20741             if(vs === false || vs === 'false'){
20742                 success = false;
20743             }
20744         }
20745         var records = [];
20746             for(var i = 0; i < c; i++){
20747                     var n = root[i];
20748                 var values = {};
20749                 var id = this.getId(n);
20750                 for(var j = 0; j < fl; j++){
20751                     f = fi[j];
20752                 var v = this.ef[j](n);
20753                 if (!f.convert) {
20754                     Roo.log('missing convert for ' + f.name);
20755                     Roo.log(f);
20756                     continue;
20757                 }
20758                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20759                 }
20760                 var record = new Record(values, id);
20761                 record.json = n;
20762                 records[i] = record;
20763             }
20764             return {
20765                 success : success,
20766                 records : records,
20767                 totalRecords : totalRecords
20768             };
20769     }
20770 });/*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781 /**
20782  * @class Roo.data.XmlReader
20783  * @extends Roo.data.DataReader
20784  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20785  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20786  * <p>
20787  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20788  * header in the HTTP response must be set to "text/xml".</em>
20789  * <p>
20790  * Example code:
20791  * <pre><code>
20792 var RecordDef = Roo.data.Record.create([
20793    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20794    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20795 ]);
20796 var myReader = new Roo.data.XmlReader({
20797    totalRecords: "results", // The element which contains the total dataset size (optional)
20798    record: "row",           // The repeated element which contains row information
20799    id: "id"                 // The element within the row that provides an ID for the record (optional)
20800 }, RecordDef);
20801 </code></pre>
20802  * <p>
20803  * This would consume an XML file like this:
20804  * <pre><code>
20805 &lt;?xml?>
20806 &lt;dataset>
20807  &lt;results>2&lt;/results>
20808  &lt;row>
20809    &lt;id>1&lt;/id>
20810    &lt;name>Bill&lt;/name>
20811    &lt;occupation>Gardener&lt;/occupation>
20812  &lt;/row>
20813  &lt;row>
20814    &lt;id>2&lt;/id>
20815    &lt;name>Ben&lt;/name>
20816    &lt;occupation>Horticulturalist&lt;/occupation>
20817  &lt;/row>
20818 &lt;/dataset>
20819 </code></pre>
20820  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20821  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20822  * paged from the remote server.
20823  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20824  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20825  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20826  * a record identifier value.
20827  * @constructor
20828  * Create a new XmlReader
20829  * @param {Object} meta Metadata configuration options
20830  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20831  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20832  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20833  */
20834 Roo.data.XmlReader = function(meta, recordType){
20835     meta = meta || {};
20836     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20837 };
20838 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20839     /**
20840      * This method is only used by a DataProxy which has retrieved data from a remote server.
20841          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20842          * to contain a method called 'responseXML' that returns an XML document object.
20843      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20844      * a cache of Roo.data.Records.
20845      */
20846     read : function(response){
20847         var doc = response.responseXML;
20848         if(!doc) {
20849             throw {message: "XmlReader.read: XML Document not available"};
20850         }
20851         return this.readRecords(doc);
20852     },
20853
20854     /**
20855      * Create a data block containing Roo.data.Records from an XML document.
20856          * @param {Object} doc A parsed XML document.
20857      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20858      * a cache of Roo.data.Records.
20859      */
20860     readRecords : function(doc){
20861         /**
20862          * After any data loads/reads, the raw XML Document is available for further custom processing.
20863          * @type XMLDocument
20864          */
20865         this.xmlData = doc;
20866         var root = doc.documentElement || doc;
20867         var q = Roo.DomQuery;
20868         var recordType = this.recordType, fields = recordType.prototype.fields;
20869         var sid = this.meta.id;
20870         var totalRecords = 0, success = true;
20871         if(this.meta.totalRecords){
20872             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20873         }
20874         
20875         if(this.meta.success){
20876             var sv = q.selectValue(this.meta.success, root, true);
20877             success = sv !== false && sv !== 'false';
20878         }
20879         var records = [];
20880         var ns = q.select(this.meta.record, root);
20881         for(var i = 0, len = ns.length; i < len; i++) {
20882                 var n = ns[i];
20883                 var values = {};
20884                 var id = sid ? q.selectValue(sid, n) : undefined;
20885                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20886                     var f = fields.items[j];
20887                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20888                     v = f.convert(v);
20889                     values[f.name] = v;
20890                 }
20891                 var record = new recordType(values, id);
20892                 record.node = n;
20893                 records[records.length] = record;
20894             }
20895
20896             return {
20897                 success : success,
20898                 records : records,
20899                 totalRecords : totalRecords || records.length
20900             };
20901     }
20902 });/*
20903  * Based on:
20904  * Ext JS Library 1.1.1
20905  * Copyright(c) 2006-2007, Ext JS, LLC.
20906  *
20907  * Originally Released Under LGPL - original licence link has changed is not relivant.
20908  *
20909  * Fork - LGPL
20910  * <script type="text/javascript">
20911  */
20912
20913 /**
20914  * @class Roo.data.ArrayReader
20915  * @extends Roo.data.DataReader
20916  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20917  * Each element of that Array represents a row of data fields. The
20918  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20919  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20920  * <p>
20921  * Example code:.
20922  * <pre><code>
20923 var RecordDef = Roo.data.Record.create([
20924     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20925     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20926 ]);
20927 var myReader = new Roo.data.ArrayReader({
20928     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20929 }, RecordDef);
20930 </code></pre>
20931  * <p>
20932  * This would consume an Array like this:
20933  * <pre><code>
20934 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20935   </code></pre>
20936  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20937  * @constructor
20938  * Create a new JsonReader
20939  * @param {Object} meta Metadata configuration options.
20940  * @param {Object} recordType Either an Array of field definition objects
20941  * as specified to {@link Roo.data.Record#create},
20942  * or an {@link Roo.data.Record} object
20943  * created using {@link Roo.data.Record#create}.
20944  */
20945 Roo.data.ArrayReader = function(meta, recordType){
20946     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20947 };
20948
20949 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20950     /**
20951      * Create a data block containing Roo.data.Records from an XML document.
20952      * @param {Object} o An Array of row objects which represents the dataset.
20953      * @return {Object} data A data block which is used by an Roo.data.Store object as
20954      * a cache of Roo.data.Records.
20955      */
20956     readRecords : function(o){
20957         var sid = this.meta ? this.meta.id : null;
20958         var recordType = this.recordType, fields = recordType.prototype.fields;
20959         var records = [];
20960         var root = o;
20961             for(var i = 0; i < root.length; i++){
20962                     var n = root[i];
20963                 var values = {};
20964                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20965                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20966                 var f = fields.items[j];
20967                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20968                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20969                 v = f.convert(v);
20970                 values[f.name] = v;
20971             }
20972                 var record = new recordType(values, id);
20973                 record.json = n;
20974                 records[records.length] = record;
20975             }
20976             return {
20977                 records : records,
20978                 totalRecords : records.length
20979             };
20980     }
20981 });/*
20982  * Based on:
20983  * Ext JS Library 1.1.1
20984  * Copyright(c) 2006-2007, Ext JS, LLC.
20985  *
20986  * Originally Released Under LGPL - original licence link has changed is not relivant.
20987  *
20988  * Fork - LGPL
20989  * <script type="text/javascript">
20990  */
20991
20992
20993 /**
20994  * @class Roo.data.Tree
20995  * @extends Roo.util.Observable
20996  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20997  * in the tree have most standard DOM functionality.
20998  * @constructor
20999  * @param {Node} root (optional) The root node
21000  */
21001 Roo.data.Tree = function(root){
21002    this.nodeHash = {};
21003    /**
21004     * The root node for this tree
21005     * @type Node
21006     */
21007    this.root = null;
21008    if(root){
21009        this.setRootNode(root);
21010    }
21011    this.addEvents({
21012        /**
21013         * @event append
21014         * Fires when a new child node is appended to a node in this tree.
21015         * @param {Tree} tree The owner tree
21016         * @param {Node} parent The parent node
21017         * @param {Node} node The newly appended node
21018         * @param {Number} index The index of the newly appended node
21019         */
21020        "append" : true,
21021        /**
21022         * @event remove
21023         * Fires when a child node is removed from a node in this tree.
21024         * @param {Tree} tree The owner tree
21025         * @param {Node} parent The parent node
21026         * @param {Node} node The child node removed
21027         */
21028        "remove" : true,
21029        /**
21030         * @event move
21031         * Fires when a node is moved to a new location in the tree
21032         * @param {Tree} tree The owner tree
21033         * @param {Node} node The node moved
21034         * @param {Node} oldParent The old parent of this node
21035         * @param {Node} newParent The new parent of this node
21036         * @param {Number} index The index it was moved to
21037         */
21038        "move" : true,
21039        /**
21040         * @event insert
21041         * Fires when a new child node is inserted in a node in this tree.
21042         * @param {Tree} tree The owner tree
21043         * @param {Node} parent The parent node
21044         * @param {Node} node The child node inserted
21045         * @param {Node} refNode The child node the node was inserted before
21046         */
21047        "insert" : true,
21048        /**
21049         * @event beforeappend
21050         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21051         * @param {Tree} tree The owner tree
21052         * @param {Node} parent The parent node
21053         * @param {Node} node The child node to be appended
21054         */
21055        "beforeappend" : true,
21056        /**
21057         * @event beforeremove
21058         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21059         * @param {Tree} tree The owner tree
21060         * @param {Node} parent The parent node
21061         * @param {Node} node The child node to be removed
21062         */
21063        "beforeremove" : true,
21064        /**
21065         * @event beforemove
21066         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21067         * @param {Tree} tree The owner tree
21068         * @param {Node} node The node being moved
21069         * @param {Node} oldParent The parent of the node
21070         * @param {Node} newParent The new parent the node is moving to
21071         * @param {Number} index The index it is being moved to
21072         */
21073        "beforemove" : true,
21074        /**
21075         * @event beforeinsert
21076         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21077         * @param {Tree} tree The owner tree
21078         * @param {Node} parent The parent node
21079         * @param {Node} node The child node to be inserted
21080         * @param {Node} refNode The child node the node is being inserted before
21081         */
21082        "beforeinsert" : true
21083    });
21084
21085     Roo.data.Tree.superclass.constructor.call(this);
21086 };
21087
21088 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21089     pathSeparator: "/",
21090
21091     proxyNodeEvent : function(){
21092         return this.fireEvent.apply(this, arguments);
21093     },
21094
21095     /**
21096      * Returns the root node for this tree.
21097      * @return {Node}
21098      */
21099     getRootNode : function(){
21100         return this.root;
21101     },
21102
21103     /**
21104      * Sets the root node for this tree.
21105      * @param {Node} node
21106      * @return {Node}
21107      */
21108     setRootNode : function(node){
21109         this.root = node;
21110         node.ownerTree = this;
21111         node.isRoot = true;
21112         this.registerNode(node);
21113         return node;
21114     },
21115
21116     /**
21117      * Gets a node in this tree by its id.
21118      * @param {String} id
21119      * @return {Node}
21120      */
21121     getNodeById : function(id){
21122         return this.nodeHash[id];
21123     },
21124
21125     registerNode : function(node){
21126         this.nodeHash[node.id] = node;
21127     },
21128
21129     unregisterNode : function(node){
21130         delete this.nodeHash[node.id];
21131     },
21132
21133     toString : function(){
21134         return "[Tree"+(this.id?" "+this.id:"")+"]";
21135     }
21136 });
21137
21138 /**
21139  * @class Roo.data.Node
21140  * @extends Roo.util.Observable
21141  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21142  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21143  * @constructor
21144  * @param {Object} attributes The attributes/config for the node
21145  */
21146 Roo.data.Node = function(attributes){
21147     /**
21148      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21149      * @type {Object}
21150      */
21151     this.attributes = attributes || {};
21152     this.leaf = this.attributes.leaf;
21153     /**
21154      * The node id. @type String
21155      */
21156     this.id = this.attributes.id;
21157     if(!this.id){
21158         this.id = Roo.id(null, "ynode-");
21159         this.attributes.id = this.id;
21160     }
21161     /**
21162      * All child nodes of this node. @type Array
21163      */
21164     this.childNodes = [];
21165     if(!this.childNodes.indexOf){ // indexOf is a must
21166         this.childNodes.indexOf = function(o){
21167             for(var i = 0, len = this.length; i < len; i++){
21168                 if(this[i] == o) {
21169                     return i;
21170                 }
21171             }
21172             return -1;
21173         };
21174     }
21175     /**
21176      * The parent node for this node. @type Node
21177      */
21178     this.parentNode = null;
21179     /**
21180      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21181      */
21182     this.firstChild = null;
21183     /**
21184      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21185      */
21186     this.lastChild = null;
21187     /**
21188      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21189      */
21190     this.previousSibling = null;
21191     /**
21192      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21193      */
21194     this.nextSibling = null;
21195
21196     this.addEvents({
21197        /**
21198         * @event append
21199         * Fires when a new child node is appended
21200         * @param {Tree} tree The owner tree
21201         * @param {Node} this This node
21202         * @param {Node} node The newly appended node
21203         * @param {Number} index The index of the newly appended node
21204         */
21205        "append" : true,
21206        /**
21207         * @event remove
21208         * Fires when a child node is removed
21209         * @param {Tree} tree The owner tree
21210         * @param {Node} this This node
21211         * @param {Node} node The removed node
21212         */
21213        "remove" : true,
21214        /**
21215         * @event move
21216         * Fires when this node is moved to a new location in the tree
21217         * @param {Tree} tree The owner tree
21218         * @param {Node} this This node
21219         * @param {Node} oldParent The old parent of this node
21220         * @param {Node} newParent The new parent of this node
21221         * @param {Number} index The index it was moved to
21222         */
21223        "move" : true,
21224        /**
21225         * @event insert
21226         * Fires when a new child node is inserted.
21227         * @param {Tree} tree The owner tree
21228         * @param {Node} this This node
21229         * @param {Node} node The child node inserted
21230         * @param {Node} refNode The child node the node was inserted before
21231         */
21232        "insert" : true,
21233        /**
21234         * @event beforeappend
21235         * Fires before a new child is appended, return false to cancel the append.
21236         * @param {Tree} tree The owner tree
21237         * @param {Node} this This node
21238         * @param {Node} node The child node to be appended
21239         */
21240        "beforeappend" : true,
21241        /**
21242         * @event beforeremove
21243         * Fires before a child is removed, return false to cancel the remove.
21244         * @param {Tree} tree The owner tree
21245         * @param {Node} this This node
21246         * @param {Node} node The child node to be removed
21247         */
21248        "beforeremove" : true,
21249        /**
21250         * @event beforemove
21251         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21252         * @param {Tree} tree The owner tree
21253         * @param {Node} this This node
21254         * @param {Node} oldParent The parent of this node
21255         * @param {Node} newParent The new parent this node is moving to
21256         * @param {Number} index The index it is being moved to
21257         */
21258        "beforemove" : true,
21259        /**
21260         * @event beforeinsert
21261         * Fires before a new child is inserted, return false to cancel the insert.
21262         * @param {Tree} tree The owner tree
21263         * @param {Node} this This node
21264         * @param {Node} node The child node to be inserted
21265         * @param {Node} refNode The child node the node is being inserted before
21266         */
21267        "beforeinsert" : true
21268    });
21269     this.listeners = this.attributes.listeners;
21270     Roo.data.Node.superclass.constructor.call(this);
21271 };
21272
21273 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21274     fireEvent : function(evtName){
21275         // first do standard event for this node
21276         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21277             return false;
21278         }
21279         // then bubble it up to the tree if the event wasn't cancelled
21280         var ot = this.getOwnerTree();
21281         if(ot){
21282             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21283                 return false;
21284             }
21285         }
21286         return true;
21287     },
21288
21289     /**
21290      * Returns true if this node is a leaf
21291      * @return {Boolean}
21292      */
21293     isLeaf : function(){
21294         return this.leaf === true;
21295     },
21296
21297     // private
21298     setFirstChild : function(node){
21299         this.firstChild = node;
21300     },
21301
21302     //private
21303     setLastChild : function(node){
21304         this.lastChild = node;
21305     },
21306
21307
21308     /**
21309      * Returns true if this node is the last child of its parent
21310      * @return {Boolean}
21311      */
21312     isLast : function(){
21313        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21314     },
21315
21316     /**
21317      * Returns true if this node is the first child of its parent
21318      * @return {Boolean}
21319      */
21320     isFirst : function(){
21321        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21322     },
21323
21324     hasChildNodes : function(){
21325         return !this.isLeaf() && this.childNodes.length > 0;
21326     },
21327
21328     /**
21329      * Insert node(s) as the last child node of this node.
21330      * @param {Node/Array} node The node or Array of nodes to append
21331      * @return {Node} The appended node if single append, or null if an array was passed
21332      */
21333     appendChild : function(node){
21334         var multi = false;
21335         if(node instanceof Array){
21336             multi = node;
21337         }else if(arguments.length > 1){
21338             multi = arguments;
21339         }
21340         // if passed an array or multiple args do them one by one
21341         if(multi){
21342             for(var i = 0, len = multi.length; i < len; i++) {
21343                 this.appendChild(multi[i]);
21344             }
21345         }else{
21346             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21347                 return false;
21348             }
21349             var index = this.childNodes.length;
21350             var oldParent = node.parentNode;
21351             // it's a move, make sure we move it cleanly
21352             if(oldParent){
21353                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21354                     return false;
21355                 }
21356                 oldParent.removeChild(node);
21357             }
21358             index = this.childNodes.length;
21359             if(index == 0){
21360                 this.setFirstChild(node);
21361             }
21362             this.childNodes.push(node);
21363             node.parentNode = this;
21364             var ps = this.childNodes[index-1];
21365             if(ps){
21366                 node.previousSibling = ps;
21367                 ps.nextSibling = node;
21368             }else{
21369                 node.previousSibling = null;
21370             }
21371             node.nextSibling = null;
21372             this.setLastChild(node);
21373             node.setOwnerTree(this.getOwnerTree());
21374             this.fireEvent("append", this.ownerTree, this, node, index);
21375             if(oldParent){
21376                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21377             }
21378             return node;
21379         }
21380     },
21381
21382     /**
21383      * Removes a child node from this node.
21384      * @param {Node} node The node to remove
21385      * @return {Node} The removed node
21386      */
21387     removeChild : function(node){
21388         var index = this.childNodes.indexOf(node);
21389         if(index == -1){
21390             return false;
21391         }
21392         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21393             return false;
21394         }
21395
21396         // remove it from childNodes collection
21397         this.childNodes.splice(index, 1);
21398
21399         // update siblings
21400         if(node.previousSibling){
21401             node.previousSibling.nextSibling = node.nextSibling;
21402         }
21403         if(node.nextSibling){
21404             node.nextSibling.previousSibling = node.previousSibling;
21405         }
21406
21407         // update child refs
21408         if(this.firstChild == node){
21409             this.setFirstChild(node.nextSibling);
21410         }
21411         if(this.lastChild == node){
21412             this.setLastChild(node.previousSibling);
21413         }
21414
21415         node.setOwnerTree(null);
21416         // clear any references from the node
21417         node.parentNode = null;
21418         node.previousSibling = null;
21419         node.nextSibling = null;
21420         this.fireEvent("remove", this.ownerTree, this, node);
21421         return node;
21422     },
21423
21424     /**
21425      * Inserts the first node before the second node in this nodes childNodes collection.
21426      * @param {Node} node The node to insert
21427      * @param {Node} refNode The node to insert before (if null the node is appended)
21428      * @return {Node} The inserted node
21429      */
21430     insertBefore : function(node, refNode){
21431         if(!refNode){ // like standard Dom, refNode can be null for append
21432             return this.appendChild(node);
21433         }
21434         // nothing to do
21435         if(node == refNode){
21436             return false;
21437         }
21438
21439         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21440             return false;
21441         }
21442         var index = this.childNodes.indexOf(refNode);
21443         var oldParent = node.parentNode;
21444         var refIndex = index;
21445
21446         // when moving internally, indexes will change after remove
21447         if(oldParent == this && this.childNodes.indexOf(node) < index){
21448             refIndex--;
21449         }
21450
21451         // it's a move, make sure we move it cleanly
21452         if(oldParent){
21453             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21454                 return false;
21455             }
21456             oldParent.removeChild(node);
21457         }
21458         if(refIndex == 0){
21459             this.setFirstChild(node);
21460         }
21461         this.childNodes.splice(refIndex, 0, node);
21462         node.parentNode = this;
21463         var ps = this.childNodes[refIndex-1];
21464         if(ps){
21465             node.previousSibling = ps;
21466             ps.nextSibling = node;
21467         }else{
21468             node.previousSibling = null;
21469         }
21470         node.nextSibling = refNode;
21471         refNode.previousSibling = node;
21472         node.setOwnerTree(this.getOwnerTree());
21473         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21474         if(oldParent){
21475             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21476         }
21477         return node;
21478     },
21479
21480     /**
21481      * Returns the child node at the specified index.
21482      * @param {Number} index
21483      * @return {Node}
21484      */
21485     item : function(index){
21486         return this.childNodes[index];
21487     },
21488
21489     /**
21490      * Replaces one child node in this node with another.
21491      * @param {Node} newChild The replacement node
21492      * @param {Node} oldChild The node to replace
21493      * @return {Node} The replaced node
21494      */
21495     replaceChild : function(newChild, oldChild){
21496         this.insertBefore(newChild, oldChild);
21497         this.removeChild(oldChild);
21498         return oldChild;
21499     },
21500
21501     /**
21502      * Returns the index of a child node
21503      * @param {Node} node
21504      * @return {Number} The index of the node or -1 if it was not found
21505      */
21506     indexOf : function(child){
21507         return this.childNodes.indexOf(child);
21508     },
21509
21510     /**
21511      * Returns the tree this node is in.
21512      * @return {Tree}
21513      */
21514     getOwnerTree : function(){
21515         // if it doesn't have one, look for one
21516         if(!this.ownerTree){
21517             var p = this;
21518             while(p){
21519                 if(p.ownerTree){
21520                     this.ownerTree = p.ownerTree;
21521                     break;
21522                 }
21523                 p = p.parentNode;
21524             }
21525         }
21526         return this.ownerTree;
21527     },
21528
21529     /**
21530      * Returns depth of this node (the root node has a depth of 0)
21531      * @return {Number}
21532      */
21533     getDepth : function(){
21534         var depth = 0;
21535         var p = this;
21536         while(p.parentNode){
21537             ++depth;
21538             p = p.parentNode;
21539         }
21540         return depth;
21541     },
21542
21543     // private
21544     setOwnerTree : function(tree){
21545         // if it's move, we need to update everyone
21546         if(tree != this.ownerTree){
21547             if(this.ownerTree){
21548                 this.ownerTree.unregisterNode(this);
21549             }
21550             this.ownerTree = tree;
21551             var cs = this.childNodes;
21552             for(var i = 0, len = cs.length; i < len; i++) {
21553                 cs[i].setOwnerTree(tree);
21554             }
21555             if(tree){
21556                 tree.registerNode(this);
21557             }
21558         }
21559     },
21560
21561     /**
21562      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21563      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21564      * @return {String} The path
21565      */
21566     getPath : function(attr){
21567         attr = attr || "id";
21568         var p = this.parentNode;
21569         var b = [this.attributes[attr]];
21570         while(p){
21571             b.unshift(p.attributes[attr]);
21572             p = p.parentNode;
21573         }
21574         var sep = this.getOwnerTree().pathSeparator;
21575         return sep + b.join(sep);
21576     },
21577
21578     /**
21579      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21580      * function call will be the scope provided or the current node. The arguments to the function
21581      * will be the args provided or the current node. If the function returns false at any point,
21582      * the bubble is stopped.
21583      * @param {Function} fn The function to call
21584      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21585      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21586      */
21587     bubble : function(fn, scope, args){
21588         var p = this;
21589         while(p){
21590             if(fn.call(scope || p, args || p) === false){
21591                 break;
21592             }
21593             p = p.parentNode;
21594         }
21595     },
21596
21597     /**
21598      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21599      * function call will be the scope provided or the current node. The arguments to the function
21600      * will be the args provided or the current node. If the function returns false at any point,
21601      * the cascade is stopped on that branch.
21602      * @param {Function} fn The function to call
21603      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21604      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21605      */
21606     cascade : function(fn, scope, args){
21607         if(fn.call(scope || this, args || this) !== false){
21608             var cs = this.childNodes;
21609             for(var i = 0, len = cs.length; i < len; i++) {
21610                 cs[i].cascade(fn, scope, args);
21611             }
21612         }
21613     },
21614
21615     /**
21616      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21617      * function call will be the scope provided or the current node. The arguments to the function
21618      * will be the args provided or the current node. If the function returns false at any point,
21619      * the iteration stops.
21620      * @param {Function} fn The function to call
21621      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21622      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21623      */
21624     eachChild : function(fn, scope, args){
21625         var cs = this.childNodes;
21626         for(var i = 0, len = cs.length; i < len; i++) {
21627                 if(fn.call(scope || this, args || cs[i]) === false){
21628                     break;
21629                 }
21630         }
21631     },
21632
21633     /**
21634      * Finds the first child that has the attribute with the specified value.
21635      * @param {String} attribute The attribute name
21636      * @param {Mixed} value The value to search for
21637      * @return {Node} The found child or null if none was found
21638      */
21639     findChild : function(attribute, value){
21640         var cs = this.childNodes;
21641         for(var i = 0, len = cs.length; i < len; i++) {
21642                 if(cs[i].attributes[attribute] == value){
21643                     return cs[i];
21644                 }
21645         }
21646         return null;
21647     },
21648
21649     /**
21650      * Finds the first child by a custom function. The child matches if the function passed
21651      * returns true.
21652      * @param {Function} fn
21653      * @param {Object} scope (optional)
21654      * @return {Node} The found child or null if none was found
21655      */
21656     findChildBy : function(fn, scope){
21657         var cs = this.childNodes;
21658         for(var i = 0, len = cs.length; i < len; i++) {
21659                 if(fn.call(scope||cs[i], cs[i]) === true){
21660                     return cs[i];
21661                 }
21662         }
21663         return null;
21664     },
21665
21666     /**
21667      * Sorts this nodes children using the supplied sort function
21668      * @param {Function} fn
21669      * @param {Object} scope (optional)
21670      */
21671     sort : function(fn, scope){
21672         var cs = this.childNodes;
21673         var len = cs.length;
21674         if(len > 0){
21675             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21676             cs.sort(sortFn);
21677             for(var i = 0; i < len; i++){
21678                 var n = cs[i];
21679                 n.previousSibling = cs[i-1];
21680                 n.nextSibling = cs[i+1];
21681                 if(i == 0){
21682                     this.setFirstChild(n);
21683                 }
21684                 if(i == len-1){
21685                     this.setLastChild(n);
21686                 }
21687             }
21688         }
21689     },
21690
21691     /**
21692      * Returns true if this node is an ancestor (at any point) of the passed node.
21693      * @param {Node} node
21694      * @return {Boolean}
21695      */
21696     contains : function(node){
21697         return node.isAncestor(this);
21698     },
21699
21700     /**
21701      * Returns true if the passed node is an ancestor (at any point) of this node.
21702      * @param {Node} node
21703      * @return {Boolean}
21704      */
21705     isAncestor : function(node){
21706         var p = this.parentNode;
21707         while(p){
21708             if(p == node){
21709                 return true;
21710             }
21711             p = p.parentNode;
21712         }
21713         return false;
21714     },
21715
21716     toString : function(){
21717         return "[Node"+(this.id?" "+this.id:"")+"]";
21718     }
21719 });/*
21720  * Based on:
21721  * Ext JS Library 1.1.1
21722  * Copyright(c) 2006-2007, Ext JS, LLC.
21723  *
21724  * Originally Released Under LGPL - original licence link has changed is not relivant.
21725  *
21726  * Fork - LGPL
21727  * <script type="text/javascript">
21728  */
21729  
21730
21731 /**
21732  * @class Roo.ComponentMgr
21733  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21734  * @singleton
21735  */
21736 Roo.ComponentMgr = function(){
21737     var all = new Roo.util.MixedCollection();
21738
21739     return {
21740         /**
21741          * Registers a component.
21742          * @param {Roo.Component} c The component
21743          */
21744         register : function(c){
21745             all.add(c);
21746         },
21747
21748         /**
21749          * Unregisters a component.
21750          * @param {Roo.Component} c The component
21751          */
21752         unregister : function(c){
21753             all.remove(c);
21754         },
21755
21756         /**
21757          * Returns a component by id
21758          * @param {String} id The component id
21759          */
21760         get : function(id){
21761             return all.get(id);
21762         },
21763
21764         /**
21765          * Registers a function that will be called when a specified component is added to ComponentMgr
21766          * @param {String} id The component id
21767          * @param {Funtction} fn The callback function
21768          * @param {Object} scope The scope of the callback
21769          */
21770         onAvailable : function(id, fn, scope){
21771             all.on("add", function(index, o){
21772                 if(o.id == id){
21773                     fn.call(scope || o, o);
21774                     all.un("add", fn, scope);
21775                 }
21776             });
21777         }
21778     };
21779 }();/*
21780  * Based on:
21781  * Ext JS Library 1.1.1
21782  * Copyright(c) 2006-2007, Ext JS, LLC.
21783  *
21784  * Originally Released Under LGPL - original licence link has changed is not relivant.
21785  *
21786  * Fork - LGPL
21787  * <script type="text/javascript">
21788  */
21789  
21790 /**
21791  * @class Roo.Component
21792  * @extends Roo.util.Observable
21793  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21794  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21795  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21796  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21797  * All visual components (widgets) that require rendering into a layout should subclass Component.
21798  * @constructor
21799  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21800  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21801  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21802  */
21803 Roo.Component = function(config){
21804     config = config || {};
21805     if(config.tagName || config.dom || typeof config == "string"){ // element object
21806         config = {el: config, id: config.id || config};
21807     }
21808     this.initialConfig = config;
21809
21810     Roo.apply(this, config);
21811     this.addEvents({
21812         /**
21813          * @event disable
21814          * Fires after the component is disabled.
21815              * @param {Roo.Component} this
21816              */
21817         disable : true,
21818         /**
21819          * @event enable
21820          * Fires after the component is enabled.
21821              * @param {Roo.Component} this
21822              */
21823         enable : true,
21824         /**
21825          * @event beforeshow
21826          * Fires before the component is shown.  Return false to stop the show.
21827              * @param {Roo.Component} this
21828              */
21829         beforeshow : true,
21830         /**
21831          * @event show
21832          * Fires after the component is shown.
21833              * @param {Roo.Component} this
21834              */
21835         show : true,
21836         /**
21837          * @event beforehide
21838          * Fires before the component is hidden. Return false to stop the hide.
21839              * @param {Roo.Component} this
21840              */
21841         beforehide : true,
21842         /**
21843          * @event hide
21844          * Fires after the component is hidden.
21845              * @param {Roo.Component} this
21846              */
21847         hide : true,
21848         /**
21849          * @event beforerender
21850          * Fires before the component is rendered. Return false to stop the render.
21851              * @param {Roo.Component} this
21852              */
21853         beforerender : true,
21854         /**
21855          * @event render
21856          * Fires after the component is rendered.
21857              * @param {Roo.Component} this
21858              */
21859         render : true,
21860         /**
21861          * @event beforedestroy
21862          * Fires before the component is destroyed. Return false to stop the destroy.
21863              * @param {Roo.Component} this
21864              */
21865         beforedestroy : true,
21866         /**
21867          * @event destroy
21868          * Fires after the component is destroyed.
21869              * @param {Roo.Component} this
21870              */
21871         destroy : true
21872     });
21873     if(!this.id){
21874         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21875     }
21876     Roo.ComponentMgr.register(this);
21877     Roo.Component.superclass.constructor.call(this);
21878     this.initComponent();
21879     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21880         this.render(this.renderTo);
21881         delete this.renderTo;
21882     }
21883 };
21884
21885 /** @private */
21886 Roo.Component.AUTO_ID = 1000;
21887
21888 Roo.extend(Roo.Component, Roo.util.Observable, {
21889     /**
21890      * @scope Roo.Component.prototype
21891      * @type {Boolean}
21892      * true if this component is hidden. Read-only.
21893      */
21894     hidden : false,
21895     /**
21896      * @type {Boolean}
21897      * true if this component is disabled. Read-only.
21898      */
21899     disabled : false,
21900     /**
21901      * @type {Boolean}
21902      * true if this component has been rendered. Read-only.
21903      */
21904     rendered : false,
21905     
21906     /** @cfg {String} disableClass
21907      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21908      */
21909     disabledClass : "x-item-disabled",
21910         /** @cfg {Boolean} allowDomMove
21911          * Whether the component can move the Dom node when rendering (defaults to true).
21912          */
21913     allowDomMove : true,
21914     /** @cfg {String} hideMode
21915      * How this component should hidden. Supported values are
21916      * "visibility" (css visibility), "offsets" (negative offset position) and
21917      * "display" (css display) - defaults to "display".
21918      */
21919     hideMode: 'display',
21920
21921     /** @private */
21922     ctype : "Roo.Component",
21923
21924     /**
21925      * @cfg {String} actionMode 
21926      * which property holds the element that used for  hide() / show() / disable() / enable()
21927      * default is 'el' 
21928      */
21929     actionMode : "el",
21930
21931     /** @private */
21932     getActionEl : function(){
21933         return this[this.actionMode];
21934     },
21935
21936     initComponent : Roo.emptyFn,
21937     /**
21938      * If this is a lazy rendering component, render it to its container element.
21939      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21940      */
21941     render : function(container, position){
21942         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21943             if(!container && this.el){
21944                 this.el = Roo.get(this.el);
21945                 container = this.el.dom.parentNode;
21946                 this.allowDomMove = false;
21947             }
21948             this.container = Roo.get(container);
21949             this.rendered = true;
21950             if(position !== undefined){
21951                 if(typeof position == 'number'){
21952                     position = this.container.dom.childNodes[position];
21953                 }else{
21954                     position = Roo.getDom(position);
21955                 }
21956             }
21957             this.onRender(this.container, position || null);
21958             if(this.cls){
21959                 this.el.addClass(this.cls);
21960                 delete this.cls;
21961             }
21962             if(this.style){
21963                 this.el.applyStyles(this.style);
21964                 delete this.style;
21965             }
21966             this.fireEvent("render", this);
21967             this.afterRender(this.container);
21968             if(this.hidden){
21969                 this.hide();
21970             }
21971             if(this.disabled){
21972                 this.disable();
21973             }
21974         }
21975         return this;
21976     },
21977
21978     /** @private */
21979     // default function is not really useful
21980     onRender : function(ct, position){
21981         if(this.el){
21982             this.el = Roo.get(this.el);
21983             if(this.allowDomMove !== false){
21984                 ct.dom.insertBefore(this.el.dom, position);
21985             }
21986         }
21987     },
21988
21989     /** @private */
21990     getAutoCreate : function(){
21991         var cfg = typeof this.autoCreate == "object" ?
21992                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21993         if(this.id && !cfg.id){
21994             cfg.id = this.id;
21995         }
21996         return cfg;
21997     },
21998
21999     /** @private */
22000     afterRender : Roo.emptyFn,
22001
22002     /**
22003      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22004      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22005      */
22006     destroy : function(){
22007         if(this.fireEvent("beforedestroy", this) !== false){
22008             this.purgeListeners();
22009             this.beforeDestroy();
22010             if(this.rendered){
22011                 this.el.removeAllListeners();
22012                 this.el.remove();
22013                 if(this.actionMode == "container"){
22014                     this.container.remove();
22015                 }
22016             }
22017             this.onDestroy();
22018             Roo.ComponentMgr.unregister(this);
22019             this.fireEvent("destroy", this);
22020         }
22021     },
22022
22023         /** @private */
22024     beforeDestroy : function(){
22025
22026     },
22027
22028         /** @private */
22029         onDestroy : function(){
22030
22031     },
22032
22033     /**
22034      * Returns the underlying {@link Roo.Element}.
22035      * @return {Roo.Element} The element
22036      */
22037     getEl : function(){
22038         return this.el;
22039     },
22040
22041     /**
22042      * Returns the id of this component.
22043      * @return {String}
22044      */
22045     getId : function(){
22046         return this.id;
22047     },
22048
22049     /**
22050      * Try to focus this component.
22051      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22052      * @return {Roo.Component} this
22053      */
22054     focus : function(selectText){
22055         if(this.rendered){
22056             this.el.focus();
22057             if(selectText === true){
22058                 this.el.dom.select();
22059             }
22060         }
22061         return this;
22062     },
22063
22064     /** @private */
22065     blur : function(){
22066         if(this.rendered){
22067             this.el.blur();
22068         }
22069         return this;
22070     },
22071
22072     /**
22073      * Disable this component.
22074      * @return {Roo.Component} this
22075      */
22076     disable : function(){
22077         if(this.rendered){
22078             this.onDisable();
22079         }
22080         this.disabled = true;
22081         this.fireEvent("disable", this);
22082         return this;
22083     },
22084
22085         // private
22086     onDisable : function(){
22087         this.getActionEl().addClass(this.disabledClass);
22088         this.el.dom.disabled = true;
22089     },
22090
22091     /**
22092      * Enable this component.
22093      * @return {Roo.Component} this
22094      */
22095     enable : function(){
22096         if(this.rendered){
22097             this.onEnable();
22098         }
22099         this.disabled = false;
22100         this.fireEvent("enable", this);
22101         return this;
22102     },
22103
22104         // private
22105     onEnable : function(){
22106         this.getActionEl().removeClass(this.disabledClass);
22107         this.el.dom.disabled = false;
22108     },
22109
22110     /**
22111      * Convenience function for setting disabled/enabled by boolean.
22112      * @param {Boolean} disabled
22113      */
22114     setDisabled : function(disabled){
22115         this[disabled ? "disable" : "enable"]();
22116     },
22117
22118     /**
22119      * Show this component.
22120      * @return {Roo.Component} this
22121      */
22122     show: function(){
22123         if(this.fireEvent("beforeshow", this) !== false){
22124             this.hidden = false;
22125             if(this.rendered){
22126                 this.onShow();
22127             }
22128             this.fireEvent("show", this);
22129         }
22130         return this;
22131     },
22132
22133     // private
22134     onShow : function(){
22135         var ae = this.getActionEl();
22136         if(this.hideMode == 'visibility'){
22137             ae.dom.style.visibility = "visible";
22138         }else if(this.hideMode == 'offsets'){
22139             ae.removeClass('x-hidden');
22140         }else{
22141             ae.dom.style.display = "";
22142         }
22143     },
22144
22145     /**
22146      * Hide this component.
22147      * @return {Roo.Component} this
22148      */
22149     hide: function(){
22150         if(this.fireEvent("beforehide", this) !== false){
22151             this.hidden = true;
22152             if(this.rendered){
22153                 this.onHide();
22154             }
22155             this.fireEvent("hide", this);
22156         }
22157         return this;
22158     },
22159
22160     // private
22161     onHide : function(){
22162         var ae = this.getActionEl();
22163         if(this.hideMode == 'visibility'){
22164             ae.dom.style.visibility = "hidden";
22165         }else if(this.hideMode == 'offsets'){
22166             ae.addClass('x-hidden');
22167         }else{
22168             ae.dom.style.display = "none";
22169         }
22170     },
22171
22172     /**
22173      * Convenience function to hide or show this component by boolean.
22174      * @param {Boolean} visible True to show, false to hide
22175      * @return {Roo.Component} this
22176      */
22177     setVisible: function(visible){
22178         if(visible) {
22179             this.show();
22180         }else{
22181             this.hide();
22182         }
22183         return this;
22184     },
22185
22186     /**
22187      * Returns true if this component is visible.
22188      */
22189     isVisible : function(){
22190         return this.getActionEl().isVisible();
22191     },
22192
22193     cloneConfig : function(overrides){
22194         overrides = overrides || {};
22195         var id = overrides.id || Roo.id();
22196         var cfg = Roo.applyIf(overrides, this.initialConfig);
22197         cfg.id = id; // prevent dup id
22198         return new this.constructor(cfg);
22199     }
22200 });/*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210  (function(){ 
22211 /**
22212  * @class Roo.Layer
22213  * @extends Roo.Element
22214  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22215  * automatic maintaining of shadow/shim positions.
22216  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22217  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22218  * you can pass a string with a CSS class name. False turns off the shadow.
22219  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22220  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22221  * @cfg {String} cls CSS class to add to the element
22222  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22223  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22224  * @constructor
22225  * @param {Object} config An object with config options.
22226  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22227  */
22228
22229 Roo.Layer = function(config, existingEl){
22230     config = config || {};
22231     var dh = Roo.DomHelper;
22232     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22233     if(existingEl){
22234         this.dom = Roo.getDom(existingEl);
22235     }
22236     if(!this.dom){
22237         var o = config.dh || {tag: "div", cls: "x-layer"};
22238         this.dom = dh.append(pel, o);
22239     }
22240     if(config.cls){
22241         this.addClass(config.cls);
22242     }
22243     this.constrain = config.constrain !== false;
22244     this.visibilityMode = Roo.Element.VISIBILITY;
22245     if(config.id){
22246         this.id = this.dom.id = config.id;
22247     }else{
22248         this.id = Roo.id(this.dom);
22249     }
22250     this.zindex = config.zindex || this.getZIndex();
22251     this.position("absolute", this.zindex);
22252     if(config.shadow){
22253         this.shadowOffset = config.shadowOffset || 4;
22254         this.shadow = new Roo.Shadow({
22255             offset : this.shadowOffset,
22256             mode : config.shadow
22257         });
22258     }else{
22259         this.shadowOffset = 0;
22260     }
22261     this.useShim = config.shim !== false && Roo.useShims;
22262     this.useDisplay = config.useDisplay;
22263     this.hide();
22264 };
22265
22266 var supr = Roo.Element.prototype;
22267
22268 // shims are shared among layer to keep from having 100 iframes
22269 var shims = [];
22270
22271 Roo.extend(Roo.Layer, Roo.Element, {
22272
22273     getZIndex : function(){
22274         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22275     },
22276
22277     getShim : function(){
22278         if(!this.useShim){
22279             return null;
22280         }
22281         if(this.shim){
22282             return this.shim;
22283         }
22284         var shim = shims.shift();
22285         if(!shim){
22286             shim = this.createShim();
22287             shim.enableDisplayMode('block');
22288             shim.dom.style.display = 'none';
22289             shim.dom.style.visibility = 'visible';
22290         }
22291         var pn = this.dom.parentNode;
22292         if(shim.dom.parentNode != pn){
22293             pn.insertBefore(shim.dom, this.dom);
22294         }
22295         shim.setStyle('z-index', this.getZIndex()-2);
22296         this.shim = shim;
22297         return shim;
22298     },
22299
22300     hideShim : function(){
22301         if(this.shim){
22302             this.shim.setDisplayed(false);
22303             shims.push(this.shim);
22304             delete this.shim;
22305         }
22306     },
22307
22308     disableShadow : function(){
22309         if(this.shadow){
22310             this.shadowDisabled = true;
22311             this.shadow.hide();
22312             this.lastShadowOffset = this.shadowOffset;
22313             this.shadowOffset = 0;
22314         }
22315     },
22316
22317     enableShadow : function(show){
22318         if(this.shadow){
22319             this.shadowDisabled = false;
22320             this.shadowOffset = this.lastShadowOffset;
22321             delete this.lastShadowOffset;
22322             if(show){
22323                 this.sync(true);
22324             }
22325         }
22326     },
22327
22328     // private
22329     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22330     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22331     sync : function(doShow){
22332         var sw = this.shadow;
22333         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22334             var sh = this.getShim();
22335
22336             var w = this.getWidth(),
22337                 h = this.getHeight();
22338
22339             var l = this.getLeft(true),
22340                 t = this.getTop(true);
22341
22342             if(sw && !this.shadowDisabled){
22343                 if(doShow && !sw.isVisible()){
22344                     sw.show(this);
22345                 }else{
22346                     sw.realign(l, t, w, h);
22347                 }
22348                 if(sh){
22349                     if(doShow){
22350                        sh.show();
22351                     }
22352                     // fit the shim behind the shadow, so it is shimmed too
22353                     var a = sw.adjusts, s = sh.dom.style;
22354                     s.left = (Math.min(l, l+a.l))+"px";
22355                     s.top = (Math.min(t, t+a.t))+"px";
22356                     s.width = (w+a.w)+"px";
22357                     s.height = (h+a.h)+"px";
22358                 }
22359             }else if(sh){
22360                 if(doShow){
22361                    sh.show();
22362                 }
22363                 sh.setSize(w, h);
22364                 sh.setLeftTop(l, t);
22365             }
22366             
22367         }
22368     },
22369
22370     // private
22371     destroy : function(){
22372         this.hideShim();
22373         if(this.shadow){
22374             this.shadow.hide();
22375         }
22376         this.removeAllListeners();
22377         var pn = this.dom.parentNode;
22378         if(pn){
22379             pn.removeChild(this.dom);
22380         }
22381         Roo.Element.uncache(this.id);
22382     },
22383
22384     remove : function(){
22385         this.destroy();
22386     },
22387
22388     // private
22389     beginUpdate : function(){
22390         this.updating = true;
22391     },
22392
22393     // private
22394     endUpdate : function(){
22395         this.updating = false;
22396         this.sync(true);
22397     },
22398
22399     // private
22400     hideUnders : function(negOffset){
22401         if(this.shadow){
22402             this.shadow.hide();
22403         }
22404         this.hideShim();
22405     },
22406
22407     // private
22408     constrainXY : function(){
22409         if(this.constrain){
22410             var vw = Roo.lib.Dom.getViewWidth(),
22411                 vh = Roo.lib.Dom.getViewHeight();
22412             var s = Roo.get(document).getScroll();
22413
22414             var xy = this.getXY();
22415             var x = xy[0], y = xy[1];   
22416             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22417             // only move it if it needs it
22418             var moved = false;
22419             // first validate right/bottom
22420             if((x + w) > vw+s.left){
22421                 x = vw - w - this.shadowOffset;
22422                 moved = true;
22423             }
22424             if((y + h) > vh+s.top){
22425                 y = vh - h - this.shadowOffset;
22426                 moved = true;
22427             }
22428             // then make sure top/left isn't negative
22429             if(x < s.left){
22430                 x = s.left;
22431                 moved = true;
22432             }
22433             if(y < s.top){
22434                 y = s.top;
22435                 moved = true;
22436             }
22437             if(moved){
22438                 if(this.avoidY){
22439                     var ay = this.avoidY;
22440                     if(y <= ay && (y+h) >= ay){
22441                         y = ay-h-5;   
22442                     }
22443                 }
22444                 xy = [x, y];
22445                 this.storeXY(xy);
22446                 supr.setXY.call(this, xy);
22447                 this.sync();
22448             }
22449         }
22450     },
22451
22452     isVisible : function(){
22453         return this.visible;    
22454     },
22455
22456     // private
22457     showAction : function(){
22458         this.visible = true; // track visibility to prevent getStyle calls
22459         if(this.useDisplay === true){
22460             this.setDisplayed("");
22461         }else if(this.lastXY){
22462             supr.setXY.call(this, this.lastXY);
22463         }else if(this.lastLT){
22464             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22465         }
22466     },
22467
22468     // private
22469     hideAction : function(){
22470         this.visible = false;
22471         if(this.useDisplay === true){
22472             this.setDisplayed(false);
22473         }else{
22474             this.setLeftTop(-10000,-10000);
22475         }
22476     },
22477
22478     // overridden Element method
22479     setVisible : function(v, a, d, c, e){
22480         if(v){
22481             this.showAction();
22482         }
22483         if(a && v){
22484             var cb = function(){
22485                 this.sync(true);
22486                 if(c){
22487                     c();
22488                 }
22489             }.createDelegate(this);
22490             supr.setVisible.call(this, true, true, d, cb, e);
22491         }else{
22492             if(!v){
22493                 this.hideUnders(true);
22494             }
22495             var cb = c;
22496             if(a){
22497                 cb = function(){
22498                     this.hideAction();
22499                     if(c){
22500                         c();
22501                     }
22502                 }.createDelegate(this);
22503             }
22504             supr.setVisible.call(this, v, a, d, cb, e);
22505             if(v){
22506                 this.sync(true);
22507             }else if(!a){
22508                 this.hideAction();
22509             }
22510         }
22511     },
22512
22513     storeXY : function(xy){
22514         delete this.lastLT;
22515         this.lastXY = xy;
22516     },
22517
22518     storeLeftTop : function(left, top){
22519         delete this.lastXY;
22520         this.lastLT = [left, top];
22521     },
22522
22523     // private
22524     beforeFx : function(){
22525         this.beforeAction();
22526         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22527     },
22528
22529     // private
22530     afterFx : function(){
22531         Roo.Layer.superclass.afterFx.apply(this, arguments);
22532         this.sync(this.isVisible());
22533     },
22534
22535     // private
22536     beforeAction : function(){
22537         if(!this.updating && this.shadow){
22538             this.shadow.hide();
22539         }
22540     },
22541
22542     // overridden Element method
22543     setLeft : function(left){
22544         this.storeLeftTop(left, this.getTop(true));
22545         supr.setLeft.apply(this, arguments);
22546         this.sync();
22547     },
22548
22549     setTop : function(top){
22550         this.storeLeftTop(this.getLeft(true), top);
22551         supr.setTop.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setLeftTop : function(left, top){
22556         this.storeLeftTop(left, top);
22557         supr.setLeftTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setXY : function(xy, a, d, c, e){
22562         this.fixDisplay();
22563         this.beforeAction();
22564         this.storeXY(xy);
22565         var cb = this.createCB(c);
22566         supr.setXY.call(this, xy, a, d, cb, e);
22567         if(!a){
22568             cb();
22569         }
22570     },
22571
22572     // private
22573     createCB : function(c){
22574         var el = this;
22575         return function(){
22576             el.constrainXY();
22577             el.sync(true);
22578             if(c){
22579                 c();
22580             }
22581         };
22582     },
22583
22584     // overridden Element method
22585     setX : function(x, a, d, c, e){
22586         this.setXY([x, this.getY()], a, d, c, e);
22587     },
22588
22589     // overridden Element method
22590     setY : function(y, a, d, c, e){
22591         this.setXY([this.getX(), y], a, d, c, e);
22592     },
22593
22594     // overridden Element method
22595     setSize : function(w, h, a, d, c, e){
22596         this.beforeAction();
22597         var cb = this.createCB(c);
22598         supr.setSize.call(this, w, h, a, d, cb, e);
22599         if(!a){
22600             cb();
22601         }
22602     },
22603
22604     // overridden Element method
22605     setWidth : function(w, a, d, c, e){
22606         this.beforeAction();
22607         var cb = this.createCB(c);
22608         supr.setWidth.call(this, w, a, d, cb, e);
22609         if(!a){
22610             cb();
22611         }
22612     },
22613
22614     // overridden Element method
22615     setHeight : function(h, a, d, c, e){
22616         this.beforeAction();
22617         var cb = this.createCB(c);
22618         supr.setHeight.call(this, h, a, d, cb, e);
22619         if(!a){
22620             cb();
22621         }
22622     },
22623
22624     // overridden Element method
22625     setBounds : function(x, y, w, h, a, d, c, e){
22626         this.beforeAction();
22627         var cb = this.createCB(c);
22628         if(!a){
22629             this.storeXY([x, y]);
22630             supr.setXY.call(this, [x, y]);
22631             supr.setSize.call(this, w, h, a, d, cb, e);
22632             cb();
22633         }else{
22634             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22635         }
22636         return this;
22637     },
22638     
22639     /**
22640      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22641      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22642      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22643      * @param {Number} zindex The new z-index to set
22644      * @return {this} The Layer
22645      */
22646     setZIndex : function(zindex){
22647         this.zindex = zindex;
22648         this.setStyle("z-index", zindex + 2);
22649         if(this.shadow){
22650             this.shadow.setZIndex(zindex + 1);
22651         }
22652         if(this.shim){
22653             this.shim.setStyle("z-index", zindex);
22654         }
22655     }
22656 });
22657 })();/*
22658  * Based on:
22659  * Ext JS Library 1.1.1
22660  * Copyright(c) 2006-2007, Ext JS, LLC.
22661  *
22662  * Originally Released Under LGPL - original licence link has changed is not relivant.
22663  *
22664  * Fork - LGPL
22665  * <script type="text/javascript">
22666  */
22667
22668
22669 /**
22670  * @class Roo.Shadow
22671  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22672  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22673  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22674  * @constructor
22675  * Create a new Shadow
22676  * @param {Object} config The config object
22677  */
22678 Roo.Shadow = function(config){
22679     Roo.apply(this, config);
22680     if(typeof this.mode != "string"){
22681         this.mode = this.defaultMode;
22682     }
22683     var o = this.offset, a = {h: 0};
22684     var rad = Math.floor(this.offset/2);
22685     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22686         case "drop":
22687             a.w = 0;
22688             a.l = a.t = o;
22689             a.t -= 1;
22690             if(Roo.isIE){
22691                 a.l -= this.offset + rad;
22692                 a.t -= this.offset + rad;
22693                 a.w -= rad;
22694                 a.h -= rad;
22695                 a.t += 1;
22696             }
22697         break;
22698         case "sides":
22699             a.w = (o*2);
22700             a.l = -o;
22701             a.t = o-1;
22702             if(Roo.isIE){
22703                 a.l -= (this.offset - rad);
22704                 a.t -= this.offset + rad;
22705                 a.l += 1;
22706                 a.w -= (this.offset - rad)*2;
22707                 a.w -= rad + 1;
22708                 a.h -= 1;
22709             }
22710         break;
22711         case "frame":
22712             a.w = a.h = (o*2);
22713             a.l = a.t = -o;
22714             a.t += 1;
22715             a.h -= 2;
22716             if(Roo.isIE){
22717                 a.l -= (this.offset - rad);
22718                 a.t -= (this.offset - rad);
22719                 a.l += 1;
22720                 a.w -= (this.offset + rad + 1);
22721                 a.h -= (this.offset + rad);
22722                 a.h += 1;
22723             }
22724         break;
22725     };
22726
22727     this.adjusts = a;
22728 };
22729
22730 Roo.Shadow.prototype = {
22731     /**
22732      * @cfg {String} mode
22733      * The shadow display mode.  Supports the following options:<br />
22734      * sides: Shadow displays on both sides and bottom only<br />
22735      * frame: Shadow displays equally on all four sides<br />
22736      * drop: Traditional bottom-right drop shadow (default)
22737      */
22738     /**
22739      * @cfg {String} offset
22740      * The number of pixels to offset the shadow from the element (defaults to 4)
22741      */
22742     offset: 4,
22743
22744     // private
22745     defaultMode: "drop",
22746
22747     /**
22748      * Displays the shadow under the target element
22749      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22750      */
22751     show : function(target){
22752         target = Roo.get(target);
22753         if(!this.el){
22754             this.el = Roo.Shadow.Pool.pull();
22755             if(this.el.dom.nextSibling != target.dom){
22756                 this.el.insertBefore(target);
22757             }
22758         }
22759         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22760         if(Roo.isIE){
22761             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22762         }
22763         this.realign(
22764             target.getLeft(true),
22765             target.getTop(true),
22766             target.getWidth(),
22767             target.getHeight()
22768         );
22769         this.el.dom.style.display = "block";
22770     },
22771
22772     /**
22773      * Returns true if the shadow is visible, else false
22774      */
22775     isVisible : function(){
22776         return this.el ? true : false;  
22777     },
22778
22779     /**
22780      * Direct alignment when values are already available. Show must be called at least once before
22781      * calling this method to ensure it is initialized.
22782      * @param {Number} left The target element left position
22783      * @param {Number} top The target element top position
22784      * @param {Number} width The target element width
22785      * @param {Number} height The target element height
22786      */
22787     realign : function(l, t, w, h){
22788         if(!this.el){
22789             return;
22790         }
22791         var a = this.adjusts, d = this.el.dom, s = d.style;
22792         var iea = 0;
22793         s.left = (l+a.l)+"px";
22794         s.top = (t+a.t)+"px";
22795         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22796  
22797         if(s.width != sws || s.height != shs){
22798             s.width = sws;
22799             s.height = shs;
22800             if(!Roo.isIE){
22801                 var cn = d.childNodes;
22802                 var sww = Math.max(0, (sw-12))+"px";
22803                 cn[0].childNodes[1].style.width = sww;
22804                 cn[1].childNodes[1].style.width = sww;
22805                 cn[2].childNodes[1].style.width = sww;
22806                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22807             }
22808         }
22809     },
22810
22811     /**
22812      * Hides this shadow
22813      */
22814     hide : function(){
22815         if(this.el){
22816             this.el.dom.style.display = "none";
22817             Roo.Shadow.Pool.push(this.el);
22818             delete this.el;
22819         }
22820     },
22821
22822     /**
22823      * Adjust the z-index of this shadow
22824      * @param {Number} zindex The new z-index
22825      */
22826     setZIndex : function(z){
22827         this.zIndex = z;
22828         if(this.el){
22829             this.el.setStyle("z-index", z);
22830         }
22831     }
22832 };
22833
22834 // Private utility class that manages the internal Shadow cache
22835 Roo.Shadow.Pool = function(){
22836     var p = [];
22837     var markup = Roo.isIE ?
22838                  '<div class="x-ie-shadow"></div>' :
22839                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22840     return {
22841         pull : function(){
22842             var sh = p.shift();
22843             if(!sh){
22844                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22845                 sh.autoBoxAdjust = false;
22846             }
22847             return sh;
22848         },
22849
22850         push : function(sh){
22851             p.push(sh);
22852         }
22853     };
22854 }();/*
22855  * Based on:
22856  * Ext JS Library 1.1.1
22857  * Copyright(c) 2006-2007, Ext JS, LLC.
22858  *
22859  * Originally Released Under LGPL - original licence link has changed is not relivant.
22860  *
22861  * Fork - LGPL
22862  * <script type="text/javascript">
22863  */
22864
22865 /**
22866  * @class Roo.BoxComponent
22867  * @extends Roo.Component
22868  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22869  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22870  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22871  * layout containers.
22872  * @constructor
22873  * @param {Roo.Element/String/Object} config The configuration options.
22874  */
22875 Roo.BoxComponent = function(config){
22876     Roo.Component.call(this, config);
22877     this.addEvents({
22878         /**
22879          * @event resize
22880          * Fires after the component is resized.
22881              * @param {Roo.Component} this
22882              * @param {Number} adjWidth The box-adjusted width that was set
22883              * @param {Number} adjHeight The box-adjusted height that was set
22884              * @param {Number} rawWidth The width that was originally specified
22885              * @param {Number} rawHeight The height that was originally specified
22886              */
22887         resize : true,
22888         /**
22889          * @event move
22890          * Fires after the component is moved.
22891              * @param {Roo.Component} this
22892              * @param {Number} x The new x position
22893              * @param {Number} y The new y position
22894              */
22895         move : true
22896     });
22897 };
22898
22899 Roo.extend(Roo.BoxComponent, Roo.Component, {
22900     // private, set in afterRender to signify that the component has been rendered
22901     boxReady : false,
22902     // private, used to defer height settings to subclasses
22903     deferHeight: false,
22904     /** @cfg {Number} width
22905      * width (optional) size of component
22906      */
22907      /** @cfg {Number} height
22908      * height (optional) size of component
22909      */
22910      
22911     /**
22912      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22913      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22914      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22915      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22916      * @return {Roo.BoxComponent} this
22917      */
22918     setSize : function(w, h){
22919         // support for standard size objects
22920         if(typeof w == 'object'){
22921             h = w.height;
22922             w = w.width;
22923         }
22924         // not rendered
22925         if(!this.boxReady){
22926             this.width = w;
22927             this.height = h;
22928             return this;
22929         }
22930
22931         // prevent recalcs when not needed
22932         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22933             return this;
22934         }
22935         this.lastSize = {width: w, height: h};
22936
22937         var adj = this.adjustSize(w, h);
22938         var aw = adj.width, ah = adj.height;
22939         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22940             var rz = this.getResizeEl();
22941             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22942                 rz.setSize(aw, ah);
22943             }else if(!this.deferHeight && ah !== undefined){
22944                 rz.setHeight(ah);
22945             }else if(aw !== undefined){
22946                 rz.setWidth(aw);
22947             }
22948             this.onResize(aw, ah, w, h);
22949             this.fireEvent('resize', this, aw, ah, w, h);
22950         }
22951         return this;
22952     },
22953
22954     /**
22955      * Gets the current size of the component's underlying element.
22956      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22957      */
22958     getSize : function(){
22959         return this.el.getSize();
22960     },
22961
22962     /**
22963      * Gets the current XY position of the component's underlying element.
22964      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22965      * @return {Array} The XY position of the element (e.g., [100, 200])
22966      */
22967     getPosition : function(local){
22968         if(local === true){
22969             return [this.el.getLeft(true), this.el.getTop(true)];
22970         }
22971         return this.xy || this.el.getXY();
22972     },
22973
22974     /**
22975      * Gets the current box measurements of the component's underlying element.
22976      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22977      * @returns {Object} box An object in the format {x, y, width, height}
22978      */
22979     getBox : function(local){
22980         var s = this.el.getSize();
22981         if(local){
22982             s.x = this.el.getLeft(true);
22983             s.y = this.el.getTop(true);
22984         }else{
22985             var xy = this.xy || this.el.getXY();
22986             s.x = xy[0];
22987             s.y = xy[1];
22988         }
22989         return s;
22990     },
22991
22992     /**
22993      * Sets the current box measurements of the component's underlying element.
22994      * @param {Object} box An object in the format {x, y, width, height}
22995      * @returns {Roo.BoxComponent} this
22996      */
22997     updateBox : function(box){
22998         this.setSize(box.width, box.height);
22999         this.setPagePosition(box.x, box.y);
23000         return this;
23001     },
23002
23003     // protected
23004     getResizeEl : function(){
23005         return this.resizeEl || this.el;
23006     },
23007
23008     // protected
23009     getPositionEl : function(){
23010         return this.positionEl || this.el;
23011     },
23012
23013     /**
23014      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23015      * This method fires the move event.
23016      * @param {Number} left The new left
23017      * @param {Number} top The new top
23018      * @returns {Roo.BoxComponent} this
23019      */
23020     setPosition : function(x, y){
23021         this.x = x;
23022         this.y = y;
23023         if(!this.boxReady){
23024             return this;
23025         }
23026         var adj = this.adjustPosition(x, y);
23027         var ax = adj.x, ay = adj.y;
23028
23029         var el = this.getPositionEl();
23030         if(ax !== undefined || ay !== undefined){
23031             if(ax !== undefined && ay !== undefined){
23032                 el.setLeftTop(ax, ay);
23033             }else if(ax !== undefined){
23034                 el.setLeft(ax);
23035             }else if(ay !== undefined){
23036                 el.setTop(ay);
23037             }
23038             this.onPosition(ax, ay);
23039             this.fireEvent('move', this, ax, ay);
23040         }
23041         return this;
23042     },
23043
23044     /**
23045      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23046      * This method fires the move event.
23047      * @param {Number} x The new x position
23048      * @param {Number} y The new y position
23049      * @returns {Roo.BoxComponent} this
23050      */
23051     setPagePosition : function(x, y){
23052         this.pageX = x;
23053         this.pageY = y;
23054         if(!this.boxReady){
23055             return;
23056         }
23057         if(x === undefined || y === undefined){ // cannot translate undefined points
23058             return;
23059         }
23060         var p = this.el.translatePoints(x, y);
23061         this.setPosition(p.left, p.top);
23062         return this;
23063     },
23064
23065     // private
23066     onRender : function(ct, position){
23067         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23068         if(this.resizeEl){
23069             this.resizeEl = Roo.get(this.resizeEl);
23070         }
23071         if(this.positionEl){
23072             this.positionEl = Roo.get(this.positionEl);
23073         }
23074     },
23075
23076     // private
23077     afterRender : function(){
23078         Roo.BoxComponent.superclass.afterRender.call(this);
23079         this.boxReady = true;
23080         this.setSize(this.width, this.height);
23081         if(this.x || this.y){
23082             this.setPosition(this.x, this.y);
23083         }
23084         if(this.pageX || this.pageY){
23085             this.setPagePosition(this.pageX, this.pageY);
23086         }
23087     },
23088
23089     /**
23090      * Force the component's size to recalculate based on the underlying element's current height and width.
23091      * @returns {Roo.BoxComponent} this
23092      */
23093     syncSize : function(){
23094         delete this.lastSize;
23095         this.setSize(this.el.getWidth(), this.el.getHeight());
23096         return this;
23097     },
23098
23099     /**
23100      * Called after the component is resized, this method is empty by default but can be implemented by any
23101      * subclass that needs to perform custom logic after a resize occurs.
23102      * @param {Number} adjWidth The box-adjusted width that was set
23103      * @param {Number} adjHeight The box-adjusted height that was set
23104      * @param {Number} rawWidth The width that was originally specified
23105      * @param {Number} rawHeight The height that was originally specified
23106      */
23107     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23108
23109     },
23110
23111     /**
23112      * Called after the component is moved, this method is empty by default but can be implemented by any
23113      * subclass that needs to perform custom logic after a move occurs.
23114      * @param {Number} x The new x position
23115      * @param {Number} y The new y position
23116      */
23117     onPosition : function(x, y){
23118
23119     },
23120
23121     // private
23122     adjustSize : function(w, h){
23123         if(this.autoWidth){
23124             w = 'auto';
23125         }
23126         if(this.autoHeight){
23127             h = 'auto';
23128         }
23129         return {width : w, height: h};
23130     },
23131
23132     // private
23133     adjustPosition : function(x, y){
23134         return {x : x, y: y};
23135     }
23136 });/*
23137  * Based on:
23138  * Ext JS Library 1.1.1
23139  * Copyright(c) 2006-2007, Ext JS, LLC.
23140  *
23141  * Originally Released Under LGPL - original licence link has changed is not relivant.
23142  *
23143  * Fork - LGPL
23144  * <script type="text/javascript">
23145  */
23146
23147
23148 /**
23149  * @class Roo.SplitBar
23150  * @extends Roo.util.Observable
23151  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23152  * <br><br>
23153  * Usage:
23154  * <pre><code>
23155 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23156                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23157 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23158 split.minSize = 100;
23159 split.maxSize = 600;
23160 split.animate = true;
23161 split.on('moved', splitterMoved);
23162 </code></pre>
23163  * @constructor
23164  * Create a new SplitBar
23165  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23166  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23167  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23168  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23169                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23170                         position of the SplitBar).
23171  */
23172 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23173     
23174     /** @private */
23175     this.el = Roo.get(dragElement, true);
23176     this.el.dom.unselectable = "on";
23177     /** @private */
23178     this.resizingEl = Roo.get(resizingElement, true);
23179
23180     /**
23181      * @private
23182      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23183      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23184      * @type Number
23185      */
23186     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23187     
23188     /**
23189      * The minimum size of the resizing element. (Defaults to 0)
23190      * @type Number
23191      */
23192     this.minSize = 0;
23193     
23194     /**
23195      * The maximum size of the resizing element. (Defaults to 2000)
23196      * @type Number
23197      */
23198     this.maxSize = 2000;
23199     
23200     /**
23201      * Whether to animate the transition to the new size
23202      * @type Boolean
23203      */
23204     this.animate = false;
23205     
23206     /**
23207      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23208      * @type Boolean
23209      */
23210     this.useShim = false;
23211     
23212     /** @private */
23213     this.shim = null;
23214     
23215     if(!existingProxy){
23216         /** @private */
23217         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23218     }else{
23219         this.proxy = Roo.get(existingProxy).dom;
23220     }
23221     /** @private */
23222     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23223     
23224     /** @private */
23225     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23226     
23227     /** @private */
23228     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23229     
23230     /** @private */
23231     this.dragSpecs = {};
23232     
23233     /**
23234      * @private The adapter to use to positon and resize elements
23235      */
23236     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23237     this.adapter.init(this);
23238     
23239     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23240         /** @private */
23241         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23242         this.el.addClass("x-splitbar-h");
23243     }else{
23244         /** @private */
23245         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23246         this.el.addClass("x-splitbar-v");
23247     }
23248     
23249     this.addEvents({
23250         /**
23251          * @event resize
23252          * Fires when the splitter is moved (alias for {@link #event-moved})
23253          * @param {Roo.SplitBar} this
23254          * @param {Number} newSize the new width or height
23255          */
23256         "resize" : true,
23257         /**
23258          * @event moved
23259          * Fires when the splitter is moved
23260          * @param {Roo.SplitBar} this
23261          * @param {Number} newSize the new width or height
23262          */
23263         "moved" : true,
23264         /**
23265          * @event beforeresize
23266          * Fires before the splitter is dragged
23267          * @param {Roo.SplitBar} this
23268          */
23269         "beforeresize" : true,
23270
23271         "beforeapply" : true
23272     });
23273
23274     Roo.util.Observable.call(this);
23275 };
23276
23277 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23278     onStartProxyDrag : function(x, y){
23279         this.fireEvent("beforeresize", this);
23280         if(!this.overlay){
23281             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23282             o.unselectable();
23283             o.enableDisplayMode("block");
23284             // all splitbars share the same overlay
23285             Roo.SplitBar.prototype.overlay = o;
23286         }
23287         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23288         this.overlay.show();
23289         Roo.get(this.proxy).setDisplayed("block");
23290         var size = this.adapter.getElementSize(this);
23291         this.activeMinSize = this.getMinimumSize();;
23292         this.activeMaxSize = this.getMaximumSize();;
23293         var c1 = size - this.activeMinSize;
23294         var c2 = Math.max(this.activeMaxSize - size, 0);
23295         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23296             this.dd.resetConstraints();
23297             this.dd.setXConstraint(
23298                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23299                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23300             );
23301             this.dd.setYConstraint(0, 0);
23302         }else{
23303             this.dd.resetConstraints();
23304             this.dd.setXConstraint(0, 0);
23305             this.dd.setYConstraint(
23306                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23307                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23308             );
23309          }
23310         this.dragSpecs.startSize = size;
23311         this.dragSpecs.startPoint = [x, y];
23312         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23313     },
23314     
23315     /** 
23316      * @private Called after the drag operation by the DDProxy
23317      */
23318     onEndProxyDrag : function(e){
23319         Roo.get(this.proxy).setDisplayed(false);
23320         var endPoint = Roo.lib.Event.getXY(e);
23321         if(this.overlay){
23322             this.overlay.hide();
23323         }
23324         var newSize;
23325         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23326             newSize = this.dragSpecs.startSize + 
23327                 (this.placement == Roo.SplitBar.LEFT ?
23328                     endPoint[0] - this.dragSpecs.startPoint[0] :
23329                     this.dragSpecs.startPoint[0] - endPoint[0]
23330                 );
23331         }else{
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.TOP ?
23334                     endPoint[1] - this.dragSpecs.startPoint[1] :
23335                     this.dragSpecs.startPoint[1] - endPoint[1]
23336                 );
23337         }
23338         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23339         if(newSize != this.dragSpecs.startSize){
23340             if(this.fireEvent('beforeapply', this, newSize) !== false){
23341                 this.adapter.setElementSize(this, newSize);
23342                 this.fireEvent("moved", this, newSize);
23343                 this.fireEvent("resize", this, newSize);
23344             }
23345         }
23346     },
23347     
23348     /**
23349      * Get the adapter this SplitBar uses
23350      * @return The adapter object
23351      */
23352     getAdapter : function(){
23353         return this.adapter;
23354     },
23355     
23356     /**
23357      * Set the adapter this SplitBar uses
23358      * @param {Object} adapter A SplitBar adapter object
23359      */
23360     setAdapter : function(adapter){
23361         this.adapter = adapter;
23362         this.adapter.init(this);
23363     },
23364     
23365     /**
23366      * Gets the minimum size for the resizing element
23367      * @return {Number} The minimum size
23368      */
23369     getMinimumSize : function(){
23370         return this.minSize;
23371     },
23372     
23373     /**
23374      * Sets the minimum size for the resizing element
23375      * @param {Number} minSize The minimum size
23376      */
23377     setMinimumSize : function(minSize){
23378         this.minSize = minSize;
23379     },
23380     
23381     /**
23382      * Gets the maximum size for the resizing element
23383      * @return {Number} The maximum size
23384      */
23385     getMaximumSize : function(){
23386         return this.maxSize;
23387     },
23388     
23389     /**
23390      * Sets the maximum size for the resizing element
23391      * @param {Number} maxSize The maximum size
23392      */
23393     setMaximumSize : function(maxSize){
23394         this.maxSize = maxSize;
23395     },
23396     
23397     /**
23398      * Sets the initialize size for the resizing element
23399      * @param {Number} size The initial size
23400      */
23401     setCurrentSize : function(size){
23402         var oldAnimate = this.animate;
23403         this.animate = false;
23404         this.adapter.setElementSize(this, size);
23405         this.animate = oldAnimate;
23406     },
23407     
23408     /**
23409      * Destroy this splitbar. 
23410      * @param {Boolean} removeEl True to remove the element
23411      */
23412     destroy : function(removeEl){
23413         if(this.shim){
23414             this.shim.remove();
23415         }
23416         this.dd.unreg();
23417         this.proxy.parentNode.removeChild(this.proxy);
23418         if(removeEl){
23419             this.el.remove();
23420         }
23421     }
23422 });
23423
23424 /**
23425  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23426  */
23427 Roo.SplitBar.createProxy = function(dir){
23428     var proxy = new Roo.Element(document.createElement("div"));
23429     proxy.unselectable();
23430     var cls = 'x-splitbar-proxy';
23431     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23432     document.body.appendChild(proxy.dom);
23433     return proxy.dom;
23434 };
23435
23436 /** 
23437  * @class Roo.SplitBar.BasicLayoutAdapter
23438  * Default Adapter. It assumes the splitter and resizing element are not positioned
23439  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23440  */
23441 Roo.SplitBar.BasicLayoutAdapter = function(){
23442 };
23443
23444 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23445     // do nothing for now
23446     init : function(s){
23447     
23448     },
23449     /**
23450      * Called before drag operations to get the current size of the resizing element. 
23451      * @param {Roo.SplitBar} s The SplitBar using this adapter
23452      */
23453      getElementSize : function(s){
23454         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23455             return s.resizingEl.getWidth();
23456         }else{
23457             return s.resizingEl.getHeight();
23458         }
23459     },
23460     
23461     /**
23462      * Called after drag operations to set the size of the resizing element.
23463      * @param {Roo.SplitBar} s The SplitBar using this adapter
23464      * @param {Number} newSize The new size to set
23465      * @param {Function} onComplete A function to be invoked when resizing is complete
23466      */
23467     setElementSize : function(s, newSize, onComplete){
23468         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23469             if(!s.animate){
23470                 s.resizingEl.setWidth(newSize);
23471                 if(onComplete){
23472                     onComplete(s, newSize);
23473                 }
23474             }else{
23475                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23476             }
23477         }else{
23478             
23479             if(!s.animate){
23480                 s.resizingEl.setHeight(newSize);
23481                 if(onComplete){
23482                     onComplete(s, newSize);
23483                 }
23484             }else{
23485                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23486             }
23487         }
23488     }
23489 };
23490
23491 /** 
23492  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23493  * @extends Roo.SplitBar.BasicLayoutAdapter
23494  * Adapter that  moves the splitter element to align with the resized sizing element. 
23495  * Used with an absolute positioned SplitBar.
23496  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23497  * document.body, make sure you assign an id to the body element.
23498  */
23499 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23500     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23501     this.container = Roo.get(container);
23502 };
23503
23504 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23505     init : function(s){
23506         this.basic.init(s);
23507     },
23508     
23509     getElementSize : function(s){
23510         return this.basic.getElementSize(s);
23511     },
23512     
23513     setElementSize : function(s, newSize, onComplete){
23514         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23515     },
23516     
23517     moveSplitter : function(s){
23518         var yes = Roo.SplitBar;
23519         switch(s.placement){
23520             case yes.LEFT:
23521                 s.el.setX(s.resizingEl.getRight());
23522                 break;
23523             case yes.RIGHT:
23524                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23525                 break;
23526             case yes.TOP:
23527                 s.el.setY(s.resizingEl.getBottom());
23528                 break;
23529             case yes.BOTTOM:
23530                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23531                 break;
23532         }
23533     }
23534 };
23535
23536 /**
23537  * Orientation constant - Create a vertical SplitBar
23538  * @static
23539  * @type Number
23540  */
23541 Roo.SplitBar.VERTICAL = 1;
23542
23543 /**
23544  * Orientation constant - Create a horizontal SplitBar
23545  * @static
23546  * @type Number
23547  */
23548 Roo.SplitBar.HORIZONTAL = 2;
23549
23550 /**
23551  * Placement constant - The resizing element is to the left of the splitter element
23552  * @static
23553  * @type Number
23554  */
23555 Roo.SplitBar.LEFT = 1;
23556
23557 /**
23558  * Placement constant - The resizing element is to the right of the splitter element
23559  * @static
23560  * @type Number
23561  */
23562 Roo.SplitBar.RIGHT = 2;
23563
23564 /**
23565  * Placement constant - The resizing element is positioned above the splitter element
23566  * @static
23567  * @type Number
23568  */
23569 Roo.SplitBar.TOP = 3;
23570
23571 /**
23572  * Placement constant - The resizing element is positioned under splitter element
23573  * @static
23574  * @type Number
23575  */
23576 Roo.SplitBar.BOTTOM = 4;
23577 /*
23578  * Based on:
23579  * Ext JS Library 1.1.1
23580  * Copyright(c) 2006-2007, Ext JS, LLC.
23581  *
23582  * Originally Released Under LGPL - original licence link has changed is not relivant.
23583  *
23584  * Fork - LGPL
23585  * <script type="text/javascript">
23586  */
23587
23588 /**
23589  * @class Roo.View
23590  * @extends Roo.util.Observable
23591  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23592  * This class also supports single and multi selection modes. <br>
23593  * Create a data model bound view:
23594  <pre><code>
23595  var store = new Roo.data.Store(...);
23596
23597  var view = new Roo.View({
23598     el : "my-element",
23599     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23600  
23601     singleSelect: true,
23602     selectedClass: "ydataview-selected",
23603     store: store
23604  });
23605
23606  // listen for node click?
23607  view.on("click", function(vw, index, node, e){
23608  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23609  });
23610
23611  // load XML data
23612  dataModel.load("foobar.xml");
23613  </code></pre>
23614  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23615  * <br><br>
23616  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23617  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23618  * 
23619  * Note: old style constructor is still suported (container, template, config)
23620  * 
23621  * @constructor
23622  * Create a new View
23623  * @param {Object} config The config object
23624  * 
23625  */
23626 Roo.View = function(config, depreciated_tpl, depreciated_config){
23627     
23628     if (typeof(depreciated_tpl) == 'undefined') {
23629         // new way.. - universal constructor.
23630         Roo.apply(this, config);
23631         this.el  = Roo.get(this.el);
23632     } else {
23633         // old format..
23634         this.el  = Roo.get(config);
23635         this.tpl = depreciated_tpl;
23636         Roo.apply(this, depreciated_config);
23637     }
23638      
23639     
23640     if(typeof(this.tpl) == "string"){
23641         this.tpl = new Roo.Template(this.tpl);
23642     } else {
23643         // support xtype ctors..
23644         this.tpl = new Roo.factory(this.tpl, Roo);
23645     }
23646     
23647     
23648     this.tpl.compile();
23649    
23650
23651      
23652     /** @private */
23653     this.addEvents({
23654         /**
23655          * @event beforeclick
23656          * Fires before a click is processed. Returns false to cancel the default action.
23657          * @param {Roo.View} this
23658          * @param {Number} index The index of the target node
23659          * @param {HTMLElement} node The target node
23660          * @param {Roo.EventObject} e The raw event object
23661          */
23662             "beforeclick" : true,
23663         /**
23664          * @event click
23665          * Fires when a template node is clicked.
23666          * @param {Roo.View} this
23667          * @param {Number} index The index of the target node
23668          * @param {HTMLElement} node The target node
23669          * @param {Roo.EventObject} e The raw event object
23670          */
23671             "click" : true,
23672         /**
23673          * @event dblclick
23674          * Fires when a template node is double clicked.
23675          * @param {Roo.View} this
23676          * @param {Number} index The index of the target node
23677          * @param {HTMLElement} node The target node
23678          * @param {Roo.EventObject} e The raw event object
23679          */
23680             "dblclick" : true,
23681         /**
23682          * @event contextmenu
23683          * Fires when a template node is right clicked.
23684          * @param {Roo.View} this
23685          * @param {Number} index The index of the target node
23686          * @param {HTMLElement} node The target node
23687          * @param {Roo.EventObject} e The raw event object
23688          */
23689             "contextmenu" : true,
23690         /**
23691          * @event selectionchange
23692          * Fires when the selected nodes change.
23693          * @param {Roo.View} this
23694          * @param {Array} selections Array of the selected nodes
23695          */
23696             "selectionchange" : true,
23697     
23698         /**
23699          * @event beforeselect
23700          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23701          * @param {Roo.View} this
23702          * @param {HTMLElement} node The node to be selected
23703          * @param {Array} selections Array of currently selected nodes
23704          */
23705             "beforeselect" : true,
23706         /**
23707          * @event preparedata
23708          * Fires on every row to render, to allow you to change the data.
23709          * @param {Roo.View} this
23710          * @param {Object} data to be rendered (change this)
23711          */
23712           "preparedata" : true
23713         });
23714
23715     this.el.on({
23716         "click": this.onClick,
23717         "dblclick": this.onDblClick,
23718         "contextmenu": this.onContextMenu,
23719         scope:this
23720     });
23721
23722     this.selections = [];
23723     this.nodes = [];
23724     this.cmp = new Roo.CompositeElementLite([]);
23725     if(this.store){
23726         this.store = Roo.factory(this.store, Roo.data);
23727         this.setStore(this.store, true);
23728     }
23729     Roo.View.superclass.constructor.call(this);
23730 };
23731
23732 Roo.extend(Roo.View, Roo.util.Observable, {
23733     
23734      /**
23735      * @cfg {Roo.data.Store} store Data store to load data from.
23736      */
23737     store : false,
23738     
23739     /**
23740      * @cfg {String|Roo.Element} el The container element.
23741      */
23742     el : '',
23743     
23744     /**
23745      * @cfg {String|Roo.Template} tpl The template used by this View 
23746      */
23747     tpl : false,
23748     
23749     /**
23750      * @cfg {String} selectedClass The css class to add to selected nodes
23751      */
23752     selectedClass : "x-view-selected",
23753      /**
23754      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23755      */
23756     emptyText : "",
23757     /**
23758      * @cfg {Boolean} multiSelect Allow multiple selection
23759      */
23760     multiSelect : false,
23761     /**
23762      * @cfg {Boolean} singleSelect Allow single selection
23763      */
23764     singleSelect:  false,
23765     
23766     /**
23767      * @cfg {Boolean} toggleSelect - selecting 
23768      */
23769     toggleSelect : false,
23770     
23771     /**
23772      * Returns the element this view is bound to.
23773      * @return {Roo.Element}
23774      */
23775     getEl : function(){
23776         return this.el;
23777     },
23778
23779     /**
23780      * Refreshes the view.
23781      */
23782     refresh : function(){
23783         var t = this.tpl;
23784         this.clearSelections();
23785         this.el.update("");
23786         var html = [];
23787         var records = this.store.getRange();
23788         if(records.length < 1){
23789             this.el.update(this.emptyText);
23790             return;
23791         }
23792         for(var i = 0, len = records.length; i < len; i++){
23793             var data = this.prepareData(records[i].data, i, records[i]);
23794             this.fireEvent("preparedata", this, data, i, records[i]);
23795             html[html.length] = t.apply(data);
23796         }
23797         this.el.update(html.join(""));
23798         this.nodes = this.el.dom.childNodes;
23799         this.updateIndexes(0);
23800     },
23801
23802     /**
23803      * Function to override to reformat the data that is sent to
23804      * the template for each node.
23805      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23806      * a JSON object for an UpdateManager bound view).
23807      */
23808     prepareData : function(data){
23809         return data;
23810     },
23811
23812     onUpdate : function(ds, record){
23813         this.clearSelections();
23814         var index = this.store.indexOf(record);
23815         var n = this.nodes[index];
23816         this.tpl.insertBefore(n, this.prepareData(record.data));
23817         n.parentNode.removeChild(n);
23818         this.updateIndexes(index, index);
23819     },
23820
23821     onAdd : function(ds, records, index){
23822         this.clearSelections();
23823         if(this.nodes.length == 0){
23824             this.refresh();
23825             return;
23826         }
23827         var n = this.nodes[index];
23828         for(var i = 0, len = records.length; i < len; i++){
23829             var d = this.prepareData(records[i].data);
23830             if(n){
23831                 this.tpl.insertBefore(n, d);
23832             }else{
23833                 this.tpl.append(this.el, d);
23834             }
23835         }
23836         this.updateIndexes(index);
23837     },
23838
23839     onRemove : function(ds, record, index){
23840         this.clearSelections();
23841         this.el.dom.removeChild(this.nodes[index]);
23842         this.updateIndexes(index);
23843     },
23844
23845     /**
23846      * Refresh an individual node.
23847      * @param {Number} index
23848      */
23849     refreshNode : function(index){
23850         this.onUpdate(this.store, this.store.getAt(index));
23851     },
23852
23853     updateIndexes : function(startIndex, endIndex){
23854         var ns = this.nodes;
23855         startIndex = startIndex || 0;
23856         endIndex = endIndex || ns.length - 1;
23857         for(var i = startIndex; i <= endIndex; i++){
23858             ns[i].nodeIndex = i;
23859         }
23860     },
23861
23862     /**
23863      * Changes the data store this view uses and refresh the view.
23864      * @param {Store} store
23865      */
23866     setStore : function(store, initial){
23867         if(!initial && this.store){
23868             this.store.un("datachanged", this.refresh);
23869             this.store.un("add", this.onAdd);
23870             this.store.un("remove", this.onRemove);
23871             this.store.un("update", this.onUpdate);
23872             this.store.un("clear", this.refresh);
23873         }
23874         if(store){
23875           
23876             store.on("datachanged", this.refresh, this);
23877             store.on("add", this.onAdd, this);
23878             store.on("remove", this.onRemove, this);
23879             store.on("update", this.onUpdate, this);
23880             store.on("clear", this.refresh, this);
23881         }
23882         
23883         if(store){
23884             this.refresh();
23885         }
23886     },
23887
23888     /**
23889      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23890      * @param {HTMLElement} node
23891      * @return {HTMLElement} The template node
23892      */
23893     findItemFromChild : function(node){
23894         var el = this.el.dom;
23895         if(!node || node.parentNode == el){
23896                     return node;
23897             }
23898             var p = node.parentNode;
23899             while(p && p != el){
23900             if(p.parentNode == el){
23901                 return p;
23902             }
23903             p = p.parentNode;
23904         }
23905             return null;
23906     },
23907
23908     /** @ignore */
23909     onClick : function(e){
23910         var item = this.findItemFromChild(e.getTarget());
23911         if(item){
23912             var index = this.indexOf(item);
23913             if(this.onItemClick(item, index, e) !== false){
23914                 this.fireEvent("click", this, index, item, e);
23915             }
23916         }else{
23917             this.clearSelections();
23918         }
23919     },
23920
23921     /** @ignore */
23922     onContextMenu : function(e){
23923         var item = this.findItemFromChild(e.getTarget());
23924         if(item){
23925             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23926         }
23927     },
23928
23929     /** @ignore */
23930     onDblClick : function(e){
23931         var item = this.findItemFromChild(e.getTarget());
23932         if(item){
23933             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23934         }
23935     },
23936
23937     onItemClick : function(item, index, e)
23938     {
23939         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23940             return false;
23941         }
23942         if (this.toggleSelect) {
23943             var m = this.isSelected(item) ? 'unselect' : 'select';
23944             Roo.log(m);
23945             var _t = this;
23946             _t[m](item, true, false);
23947             return true;
23948         }
23949         if(this.multiSelect || this.singleSelect){
23950             if(this.multiSelect && e.shiftKey && this.lastSelection){
23951                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23952             }else{
23953                 this.select(item, this.multiSelect && e.ctrlKey);
23954                 this.lastSelection = item;
23955             }
23956             e.preventDefault();
23957         }
23958         return true;
23959     },
23960
23961     /**
23962      * Get the number of selected nodes.
23963      * @return {Number}
23964      */
23965     getSelectionCount : function(){
23966         return this.selections.length;
23967     },
23968
23969     /**
23970      * Get the currently selected nodes.
23971      * @return {Array} An array of HTMLElements
23972      */
23973     getSelectedNodes : function(){
23974         return this.selections;
23975     },
23976
23977     /**
23978      * Get the indexes of the selected nodes.
23979      * @return {Array}
23980      */
23981     getSelectedIndexes : function(){
23982         var indexes = [], s = this.selections;
23983         for(var i = 0, len = s.length; i < len; i++){
23984             indexes.push(s[i].nodeIndex);
23985         }
23986         return indexes;
23987     },
23988
23989     /**
23990      * Clear all selections
23991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23992      */
23993     clearSelections : function(suppressEvent){
23994         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23995             this.cmp.elements = this.selections;
23996             this.cmp.removeClass(this.selectedClass);
23997             this.selections = [];
23998             if(!suppressEvent){
23999                 this.fireEvent("selectionchange", this, this.selections);
24000             }
24001         }
24002     },
24003
24004     /**
24005      * Returns true if the passed node is selected
24006      * @param {HTMLElement/Number} node The node or node index
24007      * @return {Boolean}
24008      */
24009     isSelected : function(node){
24010         var s = this.selections;
24011         if(s.length < 1){
24012             return false;
24013         }
24014         node = this.getNode(node);
24015         return s.indexOf(node) !== -1;
24016     },
24017
24018     /**
24019      * Selects nodes.
24020      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24021      * @param {Boolean} keepExisting (optional) true to keep existing selections
24022      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24023      */
24024     select : function(nodeInfo, keepExisting, suppressEvent){
24025         if(nodeInfo instanceof Array){
24026             if(!keepExisting){
24027                 this.clearSelections(true);
24028             }
24029             for(var i = 0, len = nodeInfo.length; i < len; i++){
24030                 this.select(nodeInfo[i], true, true);
24031             }
24032             return;
24033         } 
24034         var node = this.getNode(nodeInfo);
24035         if(!node || this.isSelected(node)){
24036             return; // already selected.
24037         }
24038         if(!keepExisting){
24039             this.clearSelections(true);
24040         }
24041         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24042             Roo.fly(node).addClass(this.selectedClass);
24043             this.selections.push(node);
24044             if(!suppressEvent){
24045                 this.fireEvent("selectionchange", this, this.selections);
24046             }
24047         }
24048         
24049         
24050     },
24051       /**
24052      * Unselects nodes.
24053      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24054      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24055      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24056      */
24057     unselect : function(nodeInfo, keepExisting, suppressEvent)
24058     {
24059         if(nodeInfo instanceof Array){
24060             Roo.each(this.selections, function(s) {
24061                 this.unselect(s, nodeInfo);
24062             }, this);
24063             return;
24064         }
24065         var node = this.getNode(nodeInfo);
24066         if(!node || !this.isSelected(node)){
24067             Roo.log("not selected");
24068             return; // not selected.
24069         }
24070         // fireevent???
24071         var ns = [];
24072         Roo.each(this.selections, function(s) {
24073             if (s == node ) {
24074                 Roo.fly(node).removeClass(this.selectedClass);
24075
24076                 return;
24077             }
24078             ns.push(s);
24079         },this);
24080         
24081         this.selections= ns;
24082         this.fireEvent("selectionchange", this, this.selections);
24083     },
24084
24085     /**
24086      * Gets a template node.
24087      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24088      * @return {HTMLElement} The node or null if it wasn't found
24089      */
24090     getNode : function(nodeInfo){
24091         if(typeof nodeInfo == "string"){
24092             return document.getElementById(nodeInfo);
24093         }else if(typeof nodeInfo == "number"){
24094             return this.nodes[nodeInfo];
24095         }
24096         return nodeInfo;
24097     },
24098
24099     /**
24100      * Gets a range template nodes.
24101      * @param {Number} startIndex
24102      * @param {Number} endIndex
24103      * @return {Array} An array of nodes
24104      */
24105     getNodes : function(start, end){
24106         var ns = this.nodes;
24107         start = start || 0;
24108         end = typeof end == "undefined" ? ns.length - 1 : end;
24109         var nodes = [];
24110         if(start <= end){
24111             for(var i = start; i <= end; i++){
24112                 nodes.push(ns[i]);
24113             }
24114         } else{
24115             for(var i = start; i >= end; i--){
24116                 nodes.push(ns[i]);
24117             }
24118         }
24119         return nodes;
24120     },
24121
24122     /**
24123      * Finds the index of the passed node
24124      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24125      * @return {Number} The index of the node or -1
24126      */
24127     indexOf : function(node){
24128         node = this.getNode(node);
24129         if(typeof node.nodeIndex == "number"){
24130             return node.nodeIndex;
24131         }
24132         var ns = this.nodes;
24133         for(var i = 0, len = ns.length; i < len; i++){
24134             if(ns[i] == node){
24135                 return i;
24136             }
24137         }
24138         return -1;
24139     }
24140 });
24141 /*
24142  * Based on:
24143  * Ext JS Library 1.1.1
24144  * Copyright(c) 2006-2007, Ext JS, LLC.
24145  *
24146  * Originally Released Under LGPL - original licence link has changed is not relivant.
24147  *
24148  * Fork - LGPL
24149  * <script type="text/javascript">
24150  */
24151
24152 /**
24153  * @class Roo.JsonView
24154  * @extends Roo.View
24155  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24156 <pre><code>
24157 var view = new Roo.JsonView({
24158     container: "my-element",
24159     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24160     multiSelect: true, 
24161     jsonRoot: "data" 
24162 });
24163
24164 // listen for node click?
24165 view.on("click", function(vw, index, node, e){
24166     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24167 });
24168
24169 // direct load of JSON data
24170 view.load("foobar.php");
24171
24172 // Example from my blog list
24173 var tpl = new Roo.Template(
24174     '&lt;div class="entry"&gt;' +
24175     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24176     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24177     "&lt;/div&gt;&lt;hr /&gt;"
24178 );
24179
24180 var moreView = new Roo.JsonView({
24181     container :  "entry-list", 
24182     template : tpl,
24183     jsonRoot: "posts"
24184 });
24185 moreView.on("beforerender", this.sortEntries, this);
24186 moreView.load({
24187     url: "/blog/get-posts.php",
24188     params: "allposts=true",
24189     text: "Loading Blog Entries..."
24190 });
24191 </code></pre>
24192
24193 * Note: old code is supported with arguments : (container, template, config)
24194
24195
24196  * @constructor
24197  * Create a new JsonView
24198  * 
24199  * @param {Object} config The config object
24200  * 
24201  */
24202 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24203     
24204     
24205     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24206
24207     var um = this.el.getUpdateManager();
24208     um.setRenderer(this);
24209     um.on("update", this.onLoad, this);
24210     um.on("failure", this.onLoadException, this);
24211
24212     /**
24213      * @event beforerender
24214      * Fires before rendering of the downloaded JSON data.
24215      * @param {Roo.JsonView} this
24216      * @param {Object} data The JSON data loaded
24217      */
24218     /**
24219      * @event load
24220      * Fires when data is loaded.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      * @param {Object} response The raw Connect response object
24224      */
24225     /**
24226      * @event loadexception
24227      * Fires when loading fails.
24228      * @param {Roo.JsonView} this
24229      * @param {Object} response The raw Connect response object
24230      */
24231     this.addEvents({
24232         'beforerender' : true,
24233         'load' : true,
24234         'loadexception' : true
24235     });
24236 };
24237 Roo.extend(Roo.JsonView, Roo.View, {
24238     /**
24239      * @type {String} The root property in the loaded JSON object that contains the data
24240      */
24241     jsonRoot : "",
24242
24243     /**
24244      * Refreshes the view.
24245      */
24246     refresh : function(){
24247         this.clearSelections();
24248         this.el.update("");
24249         var html = [];
24250         var o = this.jsonData;
24251         if(o && o.length > 0){
24252             for(var i = 0, len = o.length; i < len; i++){
24253                 var data = this.prepareData(o[i], i, o);
24254                 html[html.length] = this.tpl.apply(data);
24255             }
24256         }else{
24257             html.push(this.emptyText);
24258         }
24259         this.el.update(html.join(""));
24260         this.nodes = this.el.dom.childNodes;
24261         this.updateIndexes(0);
24262     },
24263
24264     /**
24265      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
24266      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
24267      <pre><code>
24268      view.load({
24269          url: "your-url.php",
24270          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24271          callback: yourFunction,
24272          scope: yourObject, //(optional scope)
24273          discardUrl: false,
24274          nocache: false,
24275          text: "Loading...",
24276          timeout: 30,
24277          scripts: false
24278      });
24279      </code></pre>
24280      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24281      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24282      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24283      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24284      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24285      */
24286     load : function(){
24287         var um = this.el.getUpdateManager();
24288         um.update.apply(um, arguments);
24289     },
24290
24291     render : function(el, response){
24292         this.clearSelections();
24293         this.el.update("");
24294         var o;
24295         try{
24296             o = Roo.util.JSON.decode(response.responseText);
24297             if(this.jsonRoot){
24298                 
24299                 o = o[this.jsonRoot];
24300             }
24301         } catch(e){
24302         }
24303         /**
24304          * The current JSON data or null
24305          */
24306         this.jsonData = o;
24307         this.beforeRender();
24308         this.refresh();
24309     },
24310
24311 /**
24312  * Get the number of records in the current JSON dataset
24313  * @return {Number}
24314  */
24315     getCount : function(){
24316         return this.jsonData ? this.jsonData.length : 0;
24317     },
24318
24319 /**
24320  * Returns the JSON object for the specified node(s)
24321  * @param {HTMLElement/Array} node The node or an array of nodes
24322  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24323  * you get the JSON object for the node
24324  */
24325     getNodeData : function(node){
24326         if(node instanceof Array){
24327             var data = [];
24328             for(var i = 0, len = node.length; i < len; i++){
24329                 data.push(this.getNodeData(node[i]));
24330             }
24331             return data;
24332         }
24333         return this.jsonData[this.indexOf(node)] || null;
24334     },
24335
24336     beforeRender : function(){
24337         this.snapshot = this.jsonData;
24338         if(this.sortInfo){
24339             this.sort.apply(this, this.sortInfo);
24340         }
24341         this.fireEvent("beforerender", this, this.jsonData);
24342     },
24343
24344     onLoad : function(el, o){
24345         this.fireEvent("load", this, this.jsonData, o);
24346     },
24347
24348     onLoadException : function(el, o){
24349         this.fireEvent("loadexception", this, o);
24350     },
24351
24352 /**
24353  * Filter the data by a specific property.
24354  * @param {String} property A property on your JSON objects
24355  * @param {String/RegExp} value Either string that the property values
24356  * should start with, or a RegExp to test against the property
24357  */
24358     filter : function(property, value){
24359         if(this.jsonData){
24360             var data = [];
24361             var ss = this.snapshot;
24362             if(typeof value == "string"){
24363                 var vlen = value.length;
24364                 if(vlen == 0){
24365                     this.clearFilter();
24366                     return;
24367                 }
24368                 value = value.toLowerCase();
24369                 for(var i = 0, len = ss.length; i < len; i++){
24370                     var o = ss[i];
24371                     if(o[property].substr(0, vlen).toLowerCase() == value){
24372                         data.push(o);
24373                     }
24374                 }
24375             } else if(value.exec){ // regex?
24376                 for(var i = 0, len = ss.length; i < len; i++){
24377                     var o = ss[i];
24378                     if(value.test(o[property])){
24379                         data.push(o);
24380                     }
24381                 }
24382             } else{
24383                 return;
24384             }
24385             this.jsonData = data;
24386             this.refresh();
24387         }
24388     },
24389
24390 /**
24391  * Filter by a function. The passed function will be called with each
24392  * object in the current dataset. If the function returns true the value is kept,
24393  * otherwise it is filtered.
24394  * @param {Function} fn
24395  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24396  */
24397     filterBy : function(fn, scope){
24398         if(this.jsonData){
24399             var data = [];
24400             var ss = this.snapshot;
24401             for(var i = 0, len = ss.length; i < len; i++){
24402                 var o = ss[i];
24403                 if(fn.call(scope || this, o)){
24404                     data.push(o);
24405                 }
24406             }
24407             this.jsonData = data;
24408             this.refresh();
24409         }
24410     },
24411
24412 /**
24413  * Clears the current filter.
24414  */
24415     clearFilter : function(){
24416         if(this.snapshot && this.jsonData != this.snapshot){
24417             this.jsonData = this.snapshot;
24418             this.refresh();
24419         }
24420     },
24421
24422
24423 /**
24424  * Sorts the data for this view and refreshes it.
24425  * @param {String} property A property on your JSON objects to sort on
24426  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24427  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24428  */
24429     sort : function(property, dir, sortType){
24430         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24431         if(this.jsonData){
24432             var p = property;
24433             var dsc = dir && dir.toLowerCase() == "desc";
24434             var f = function(o1, o2){
24435                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24436                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24437                 ;
24438                 if(v1 < v2){
24439                     return dsc ? +1 : -1;
24440                 } else if(v1 > v2){
24441                     return dsc ? -1 : +1;
24442                 } else{
24443                     return 0;
24444                 }
24445             };
24446             this.jsonData.sort(f);
24447             this.refresh();
24448             if(this.jsonData != this.snapshot){
24449                 this.snapshot.sort(f);
24450             }
24451         }
24452     }
24453 });/*
24454  * Based on:
24455  * Ext JS Library 1.1.1
24456  * Copyright(c) 2006-2007, Ext JS, LLC.
24457  *
24458  * Originally Released Under LGPL - original licence link has changed is not relivant.
24459  *
24460  * Fork - LGPL
24461  * <script type="text/javascript">
24462  */
24463  
24464
24465 /**
24466  * @class Roo.ColorPalette
24467  * @extends Roo.Component
24468  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24469  * Here's an example of typical usage:
24470  * <pre><code>
24471 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24472 cp.render('my-div');
24473
24474 cp.on('select', function(palette, selColor){
24475     // do something with selColor
24476 });
24477 </code></pre>
24478  * @constructor
24479  * Create a new ColorPalette
24480  * @param {Object} config The config object
24481  */
24482 Roo.ColorPalette = function(config){
24483     Roo.ColorPalette.superclass.constructor.call(this, config);
24484     this.addEvents({
24485         /**
24486              * @event select
24487              * Fires when a color is selected
24488              * @param {ColorPalette} this
24489              * @param {String} color The 6-digit color hex code (without the # symbol)
24490              */
24491         select: true
24492     });
24493
24494     if(this.handler){
24495         this.on("select", this.handler, this.scope, true);
24496     }
24497 };
24498 Roo.extend(Roo.ColorPalette, Roo.Component, {
24499     /**
24500      * @cfg {String} itemCls
24501      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24502      */
24503     itemCls : "x-color-palette",
24504     /**
24505      * @cfg {String} value
24506      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24507      * the hex codes are case-sensitive.
24508      */
24509     value : null,
24510     clickEvent:'click',
24511     // private
24512     ctype: "Roo.ColorPalette",
24513
24514     /**
24515      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24516      */
24517     allowReselect : false,
24518
24519     /**
24520      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24521      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24522      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24523      * of colors with the width setting until the box is symmetrical.</p>
24524      * <p>You can override individual colors if needed:</p>
24525      * <pre><code>
24526 var cp = new Roo.ColorPalette();
24527 cp.colors[0] = "FF0000";  // change the first box to red
24528 </code></pre>
24529
24530 Or you can provide a custom array of your own for complete control:
24531 <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors = ["000000", "993300", "333300"];
24534 </code></pre>
24535      * @type Array
24536      */
24537     colors : [
24538         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24539         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24540         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24541         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24542         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24543     ],
24544
24545     // private
24546     onRender : function(container, position){
24547         var t = new Roo.MasterTemplate(
24548             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24549         );
24550         var c = this.colors;
24551         for(var i = 0, len = c.length; i < len; i++){
24552             t.add([c[i]]);
24553         }
24554         var el = document.createElement("div");
24555         el.className = this.itemCls;
24556         t.overwrite(el);
24557         container.dom.insertBefore(el, position);
24558         this.el = Roo.get(el);
24559         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24560         if(this.clickEvent != 'click'){
24561             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24562         }
24563     },
24564
24565     // private
24566     afterRender : function(){
24567         Roo.ColorPalette.superclass.afterRender.call(this);
24568         if(this.value){
24569             var s = this.value;
24570             this.value = null;
24571             this.select(s);
24572         }
24573     },
24574
24575     // private
24576     handleClick : function(e, t){
24577         e.preventDefault();
24578         if(!this.disabled){
24579             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24580             this.select(c.toUpperCase());
24581         }
24582     },
24583
24584     /**
24585      * Selects the specified color in the palette (fires the select event)
24586      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24587      */
24588     select : function(color){
24589         color = color.replace("#", "");
24590         if(color != this.value || this.allowReselect){
24591             var el = this.el;
24592             if(this.value){
24593                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24594             }
24595             el.child("a.color-"+color).addClass("x-color-palette-sel");
24596             this.value = color;
24597             this.fireEvent("select", this, color);
24598         }
24599     }
24600 });/*
24601  * Based on:
24602  * Ext JS Library 1.1.1
24603  * Copyright(c) 2006-2007, Ext JS, LLC.
24604  *
24605  * Originally Released Under LGPL - original licence link has changed is not relivant.
24606  *
24607  * Fork - LGPL
24608  * <script type="text/javascript">
24609  */
24610  
24611 /**
24612  * @class Roo.DatePicker
24613  * @extends Roo.Component
24614  * Simple date picker class.
24615  * @constructor
24616  * Create a new DatePicker
24617  * @param {Object} config The config object
24618  */
24619 Roo.DatePicker = function(config){
24620     Roo.DatePicker.superclass.constructor.call(this, config);
24621
24622     this.value = config && config.value ?
24623                  config.value.clearTime() : new Date().clearTime();
24624
24625     this.addEvents({
24626         /**
24627              * @event select
24628              * Fires when a date is selected
24629              * @param {DatePicker} this
24630              * @param {Date} date The selected date
24631              */
24632         'select': true,
24633         /**
24634              * @event monthchange
24635              * Fires when the displayed month changes 
24636              * @param {DatePicker} this
24637              * @param {Date} date The selected month
24638              */
24639         'monthchange': true
24640     });
24641
24642     if(this.handler){
24643         this.on("select", this.handler,  this.scope || this);
24644     }
24645     // build the disabledDatesRE
24646     if(!this.disabledDatesRE && this.disabledDates){
24647         var dd = this.disabledDates;
24648         var re = "(?:";
24649         for(var i = 0; i < dd.length; i++){
24650             re += dd[i];
24651             if(i != dd.length-1) re += "|";
24652         }
24653         this.disabledDatesRE = new RegExp(re + ")");
24654     }
24655 };
24656
24657 Roo.extend(Roo.DatePicker, Roo.Component, {
24658     /**
24659      * @cfg {String} todayText
24660      * The text to display on the button that selects the current date (defaults to "Today")
24661      */
24662     todayText : "Today",
24663     /**
24664      * @cfg {String} okText
24665      * The text to display on the ok button
24666      */
24667     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24668     /**
24669      * @cfg {String} cancelText
24670      * The text to display on the cancel button
24671      */
24672     cancelText : "Cancel",
24673     /**
24674      * @cfg {String} todayTip
24675      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24676      */
24677     todayTip : "{0} (Spacebar)",
24678     /**
24679      * @cfg {Date} minDate
24680      * Minimum allowable date (JavaScript date object, defaults to null)
24681      */
24682     minDate : null,
24683     /**
24684      * @cfg {Date} maxDate
24685      * Maximum allowable date (JavaScript date object, defaults to null)
24686      */
24687     maxDate : null,
24688     /**
24689      * @cfg {String} minText
24690      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24691      */
24692     minText : "This date is before the minimum date",
24693     /**
24694      * @cfg {String} maxText
24695      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24696      */
24697     maxText : "This date is after the maximum date",
24698     /**
24699      * @cfg {String} format
24700      * The default date format string which can be overriden for localization support.  The format must be
24701      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24702      */
24703     format : "m/d/y",
24704     /**
24705      * @cfg {Array} disabledDays
24706      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24707      */
24708     disabledDays : null,
24709     /**
24710      * @cfg {String} disabledDaysText
24711      * The tooltip to display when the date falls on a disabled day (defaults to "")
24712      */
24713     disabledDaysText : "",
24714     /**
24715      * @cfg {RegExp} disabledDatesRE
24716      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24717      */
24718     disabledDatesRE : null,
24719     /**
24720      * @cfg {String} disabledDatesText
24721      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24722      */
24723     disabledDatesText : "",
24724     /**
24725      * @cfg {Boolean} constrainToViewport
24726      * True to constrain the date picker to the viewport (defaults to true)
24727      */
24728     constrainToViewport : true,
24729     /**
24730      * @cfg {Array} monthNames
24731      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24732      */
24733     monthNames : Date.monthNames,
24734     /**
24735      * @cfg {Array} dayNames
24736      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24737      */
24738     dayNames : Date.dayNames,
24739     /**
24740      * @cfg {String} nextText
24741      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24742      */
24743     nextText: 'Next Month (Control+Right)',
24744     /**
24745      * @cfg {String} prevText
24746      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24747      */
24748     prevText: 'Previous Month (Control+Left)',
24749     /**
24750      * @cfg {String} monthYearText
24751      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24752      */
24753     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24754     /**
24755      * @cfg {Number} startDay
24756      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24757      */
24758     startDay : 0,
24759     /**
24760      * @cfg {Bool} showClear
24761      * Show a clear button (usefull for date form elements that can be blank.)
24762      */
24763     
24764     showClear: false,
24765     
24766     /**
24767      * Sets the value of the date field
24768      * @param {Date} value The date to set
24769      */
24770     setValue : function(value){
24771         var old = this.value;
24772         this.value = value.clearTime(true);
24773         if(this.el){
24774             this.update(this.value);
24775         }
24776     },
24777
24778     /**
24779      * Gets the current selected value of the date field
24780      * @return {Date} The selected date
24781      */
24782     getValue : function(){
24783         return this.value;
24784     },
24785
24786     // private
24787     focus : function(){
24788         if(this.el){
24789             this.update(this.activeDate);
24790         }
24791     },
24792
24793     // private
24794     onRender : function(container, position){
24795         var m = [
24796              '<table cellspacing="0">',
24797                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24798                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24799         var dn = this.dayNames;
24800         for(var i = 0; i < 7; i++){
24801             var d = this.startDay+i;
24802             if(d > 6){
24803                 d = d-7;
24804             }
24805             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24806         }
24807         m[m.length] = "</tr></thead><tbody><tr>";
24808         for(var i = 0; i < 42; i++) {
24809             if(i % 7 == 0 && i != 0){
24810                 m[m.length] = "</tr><tr>";
24811             }
24812             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24813         }
24814         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24815             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24816
24817         var el = document.createElement("div");
24818         el.className = "x-date-picker";
24819         el.innerHTML = m.join("");
24820
24821         container.dom.insertBefore(el, position);
24822
24823         this.el = Roo.get(el);
24824         this.eventEl = Roo.get(el.firstChild);
24825
24826         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24827             handler: this.showPrevMonth,
24828             scope: this,
24829             preventDefault:true,
24830             stopDefault:true
24831         });
24832
24833         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24834             handler: this.showNextMonth,
24835             scope: this,
24836             preventDefault:true,
24837             stopDefault:true
24838         });
24839
24840         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24841
24842         this.monthPicker = this.el.down('div.x-date-mp');
24843         this.monthPicker.enableDisplayMode('block');
24844         
24845         var kn = new Roo.KeyNav(this.eventEl, {
24846             "left" : function(e){
24847                 e.ctrlKey ?
24848                     this.showPrevMonth() :
24849                     this.update(this.activeDate.add("d", -1));
24850             },
24851
24852             "right" : function(e){
24853                 e.ctrlKey ?
24854                     this.showNextMonth() :
24855                     this.update(this.activeDate.add("d", 1));
24856             },
24857
24858             "up" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextYear() :
24861                     this.update(this.activeDate.add("d", -7));
24862             },
24863
24864             "down" : function(e){
24865                 e.ctrlKey ?
24866                     this.showPrevYear() :
24867                     this.update(this.activeDate.add("d", 7));
24868             },
24869
24870             "pageUp" : function(e){
24871                 this.showNextMonth();
24872             },
24873
24874             "pageDown" : function(e){
24875                 this.showPrevMonth();
24876             },
24877
24878             "enter" : function(e){
24879                 e.stopPropagation();
24880                 return true;
24881             },
24882
24883             scope : this
24884         });
24885
24886         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24887
24888         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24889
24890         this.el.unselectable();
24891         
24892         this.cells = this.el.select("table.x-date-inner tbody td");
24893         this.textNodes = this.el.query("table.x-date-inner tbody span");
24894
24895         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24896             text: "&#160;",
24897             tooltip: this.monthYearText
24898         });
24899
24900         this.mbtn.on('click', this.showMonthPicker, this);
24901         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24902
24903
24904         var today = (new Date()).dateFormat(this.format);
24905         
24906         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24907         if (this.showClear) {
24908             baseTb.add( new Roo.Toolbar.Fill());
24909         }
24910         baseTb.add({
24911             text: String.format(this.todayText, today),
24912             tooltip: String.format(this.todayTip, today),
24913             handler: this.selectToday,
24914             scope: this
24915         });
24916         
24917         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24918             
24919         //});
24920         if (this.showClear) {
24921             
24922             baseTb.add( new Roo.Toolbar.Fill());
24923             baseTb.add({
24924                 text: '&#160;',
24925                 cls: 'x-btn-icon x-btn-clear',
24926                 handler: function() {
24927                     //this.value = '';
24928                     this.fireEvent("select", this, '');
24929                 },
24930                 scope: this
24931             });
24932         }
24933         
24934         
24935         if(Roo.isIE){
24936             this.el.repaint();
24937         }
24938         this.update(this.value);
24939     },
24940
24941     createMonthPicker : function(){
24942         if(!this.monthPicker.dom.firstChild){
24943             var buf = ['<table border="0" cellspacing="0">'];
24944             for(var i = 0; i < 6; i++){
24945                 buf.push(
24946                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24947                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24948                     i == 0 ?
24949                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24950                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24951                 );
24952             }
24953             buf.push(
24954                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24955                     this.okText,
24956                     '</button><button type="button" class="x-date-mp-cancel">',
24957                     this.cancelText,
24958                     '</button></td></tr>',
24959                 '</table>'
24960             );
24961             this.monthPicker.update(buf.join(''));
24962             this.monthPicker.on('click', this.onMonthClick, this);
24963             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24964
24965             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24966             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24967
24968             this.mpMonths.each(function(m, a, i){
24969                 i += 1;
24970                 if((i%2) == 0){
24971                     m.dom.xmonth = 5 + Math.round(i * .5);
24972                 }else{
24973                     m.dom.xmonth = Math.round((i-1) * .5);
24974                 }
24975             });
24976         }
24977     },
24978
24979     showMonthPicker : function(){
24980         this.createMonthPicker();
24981         var size = this.el.getSize();
24982         this.monthPicker.setSize(size);
24983         this.monthPicker.child('table').setSize(size);
24984
24985         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24986         this.updateMPMonth(this.mpSelMonth);
24987         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24988         this.updateMPYear(this.mpSelYear);
24989
24990         this.monthPicker.slideIn('t', {duration:.2});
24991     },
24992
24993     updateMPYear : function(y){
24994         this.mpyear = y;
24995         var ys = this.mpYears.elements;
24996         for(var i = 1; i <= 10; i++){
24997             var td = ys[i-1], y2;
24998             if((i%2) == 0){
24999                 y2 = y + Math.round(i * .5);
25000                 td.firstChild.innerHTML = y2;
25001                 td.xyear = y2;
25002             }else{
25003                 y2 = y - (5-Math.round(i * .5));
25004                 td.firstChild.innerHTML = y2;
25005                 td.xyear = y2;
25006             }
25007             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25008         }
25009     },
25010
25011     updateMPMonth : function(sm){
25012         this.mpMonths.each(function(m, a, i){
25013             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         });
25015     },
25016
25017     selectMPMonth: function(m){
25018         
25019     },
25020
25021     onMonthClick : function(e, t){
25022         e.stopEvent();
25023         var el = new Roo.Element(t), pn;
25024         if(el.is('button.x-date-mp-cancel')){
25025             this.hideMonthPicker();
25026         }
25027         else if(el.is('button.x-date-mp-ok')){
25028             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25029             this.hideMonthPicker();
25030         }
25031         else if(pn = el.up('td.x-date-mp-month', 2)){
25032             this.mpMonths.removeClass('x-date-mp-sel');
25033             pn.addClass('x-date-mp-sel');
25034             this.mpSelMonth = pn.dom.xmonth;
25035         }
25036         else if(pn = el.up('td.x-date-mp-year', 2)){
25037             this.mpYears.removeClass('x-date-mp-sel');
25038             pn.addClass('x-date-mp-sel');
25039             this.mpSelYear = pn.dom.xyear;
25040         }
25041         else if(el.is('a.x-date-mp-prev')){
25042             this.updateMPYear(this.mpyear-10);
25043         }
25044         else if(el.is('a.x-date-mp-next')){
25045             this.updateMPYear(this.mpyear+10);
25046         }
25047     },
25048
25049     onMonthDblClick : function(e, t){
25050         e.stopEvent();
25051         var el = new Roo.Element(t), pn;
25052         if(pn = el.up('td.x-date-mp-month', 2)){
25053             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25054             this.hideMonthPicker();
25055         }
25056         else if(pn = el.up('td.x-date-mp-year', 2)){
25057             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25058             this.hideMonthPicker();
25059         }
25060     },
25061
25062     hideMonthPicker : function(disableAnim){
25063         if(this.monthPicker){
25064             if(disableAnim === true){
25065                 this.monthPicker.hide();
25066             }else{
25067                 this.monthPicker.slideOut('t', {duration:.2});
25068             }
25069         }
25070     },
25071
25072     // private
25073     showPrevMonth : function(e){
25074         this.update(this.activeDate.add("mo", -1));
25075     },
25076
25077     // private
25078     showNextMonth : function(e){
25079         this.update(this.activeDate.add("mo", 1));
25080     },
25081
25082     // private
25083     showPrevYear : function(){
25084         this.update(this.activeDate.add("y", -1));
25085     },
25086
25087     // private
25088     showNextYear : function(){
25089         this.update(this.activeDate.add("y", 1));
25090     },
25091
25092     // private
25093     handleMouseWheel : function(e){
25094         var delta = e.getWheelDelta();
25095         if(delta > 0){
25096             this.showPrevMonth();
25097             e.stopEvent();
25098         } else if(delta < 0){
25099             this.showNextMonth();
25100             e.stopEvent();
25101         }
25102     },
25103
25104     // private
25105     handleDateClick : function(e, t){
25106         e.stopEvent();
25107         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25108             this.setValue(new Date(t.dateValue));
25109             this.fireEvent("select", this, this.value);
25110         }
25111     },
25112
25113     // private
25114     selectToday : function(){
25115         this.setValue(new Date().clearTime());
25116         this.fireEvent("select", this, this.value);
25117     },
25118
25119     // private
25120     update : function(date)
25121     {
25122         var vd = this.activeDate;
25123         this.activeDate = date;
25124         if(vd && this.el){
25125             var t = date.getTime();
25126             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25127                 this.cells.removeClass("x-date-selected");
25128                 this.cells.each(function(c){
25129                    if(c.dom.firstChild.dateValue == t){
25130                        c.addClass("x-date-selected");
25131                        setTimeout(function(){
25132                             try{c.dom.firstChild.focus();}catch(e){}
25133                        }, 50);
25134                        return false;
25135                    }
25136                 });
25137                 return;
25138             }
25139         }
25140         
25141         var days = date.getDaysInMonth();
25142         var firstOfMonth = date.getFirstDateOfMonth();
25143         var startingPos = firstOfMonth.getDay()-this.startDay;
25144
25145         if(startingPos <= this.startDay){
25146             startingPos += 7;
25147         }
25148
25149         var pm = date.add("mo", -1);
25150         var prevStart = pm.getDaysInMonth()-startingPos;
25151
25152         var cells = this.cells.elements;
25153         var textEls = this.textNodes;
25154         days += startingPos;
25155
25156         // convert everything to numbers so it's fast
25157         var day = 86400000;
25158         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25159         var today = new Date().clearTime().getTime();
25160         var sel = date.clearTime().getTime();
25161         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25162         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25163         var ddMatch = this.disabledDatesRE;
25164         var ddText = this.disabledDatesText;
25165         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25166         var ddaysText = this.disabledDaysText;
25167         var format = this.format;
25168
25169         var setCellClass = function(cal, cell){
25170             cell.title = "";
25171             var t = d.getTime();
25172             cell.firstChild.dateValue = t;
25173             if(t == today){
25174                 cell.className += " x-date-today";
25175                 cell.title = cal.todayText;
25176             }
25177             if(t == sel){
25178                 cell.className += " x-date-selected";
25179                 setTimeout(function(){
25180                     try{cell.firstChild.focus();}catch(e){}
25181                 }, 50);
25182             }
25183             // disabling
25184             if(t < min) {
25185                 cell.className = " x-date-disabled";
25186                 cell.title = cal.minText;
25187                 return;
25188             }
25189             if(t > max) {
25190                 cell.className = " x-date-disabled";
25191                 cell.title = cal.maxText;
25192                 return;
25193             }
25194             if(ddays){
25195                 if(ddays.indexOf(d.getDay()) != -1){
25196                     cell.title = ddaysText;
25197                     cell.className = " x-date-disabled";
25198                 }
25199             }
25200             if(ddMatch && format){
25201                 var fvalue = d.dateFormat(format);
25202                 if(ddMatch.test(fvalue)){
25203                     cell.title = ddText.replace("%0", fvalue);
25204                     cell.className = " x-date-disabled";
25205                 }
25206             }
25207         };
25208
25209         var i = 0;
25210         for(; i < startingPos; i++) {
25211             textEls[i].innerHTML = (++prevStart);
25212             d.setDate(d.getDate()+1);
25213             cells[i].className = "x-date-prevday";
25214             setCellClass(this, cells[i]);
25215         }
25216         for(; i < days; i++){
25217             intDay = i - startingPos + 1;
25218             textEls[i].innerHTML = (intDay);
25219             d.setDate(d.getDate()+1);
25220             cells[i].className = "x-date-active";
25221             setCellClass(this, cells[i]);
25222         }
25223         var extraDays = 0;
25224         for(; i < 42; i++) {
25225              textEls[i].innerHTML = (++extraDays);
25226              d.setDate(d.getDate()+1);
25227              cells[i].className = "x-date-nextday";
25228              setCellClass(this, cells[i]);
25229         }
25230
25231         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25232         this.fireEvent('monthchange', this, date);
25233         
25234         if(!this.internalRender){
25235             var main = this.el.dom.firstChild;
25236             var w = main.offsetWidth;
25237             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25238             Roo.fly(main).setWidth(w);
25239             this.internalRender = true;
25240             // opera does not respect the auto grow header center column
25241             // then, after it gets a width opera refuses to recalculate
25242             // without a second pass
25243             if(Roo.isOpera && !this.secondPass){
25244                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25245                 this.secondPass = true;
25246                 this.update.defer(10, this, [date]);
25247             }
25248         }
25249         
25250         
25251     }
25252 });        /*
25253  * Based on:
25254  * Ext JS Library 1.1.1
25255  * Copyright(c) 2006-2007, Ext JS, LLC.
25256  *
25257  * Originally Released Under LGPL - original licence link has changed is not relivant.
25258  *
25259  * Fork - LGPL
25260  * <script type="text/javascript">
25261  */
25262 /**
25263  * @class Roo.TabPanel
25264  * @extends Roo.util.Observable
25265  * A lightweight tab container.
25266  * <br><br>
25267  * Usage:
25268  * <pre><code>
25269 // basic tabs 1, built from existing content
25270 var tabs = new Roo.TabPanel("tabs1");
25271 tabs.addTab("script", "View Script");
25272 tabs.addTab("markup", "View Markup");
25273 tabs.activate("script");
25274
25275 // more advanced tabs, built from javascript
25276 var jtabs = new Roo.TabPanel("jtabs");
25277 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25278
25279 // set up the UpdateManager
25280 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25281 var updater = tab2.getUpdateManager();
25282 updater.setDefaultUrl("ajax1.htm");
25283 tab2.on('activate', updater.refresh, updater, true);
25284
25285 // Use setUrl for Ajax loading
25286 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25287 tab3.setUrl("ajax2.htm", null, true);
25288
25289 // Disabled tab
25290 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25291 tab4.disable();
25292
25293 jtabs.activate("jtabs-1");
25294  * </code></pre>
25295  * @constructor
25296  * Create a new TabPanel.
25297  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25298  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25299  */
25300 Roo.TabPanel = function(container, config){
25301     /**
25302     * The container element for this TabPanel.
25303     * @type Roo.Element
25304     */
25305     this.el = Roo.get(container, true);
25306     if(config){
25307         if(typeof config == "boolean"){
25308             this.tabPosition = config ? "bottom" : "top";
25309         }else{
25310             Roo.apply(this, config);
25311         }
25312     }
25313     if(this.tabPosition == "bottom"){
25314         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25315         this.el.addClass("x-tabs-bottom");
25316     }
25317     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25318     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25319     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25320     if(Roo.isIE){
25321         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25322     }
25323     if(this.tabPosition != "bottom"){
25324         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25325          * @type Roo.Element
25326          */
25327         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25328         this.el.addClass("x-tabs-top");
25329     }
25330     this.items = [];
25331
25332     this.bodyEl.setStyle("position", "relative");
25333
25334     this.active = null;
25335     this.activateDelegate = this.activate.createDelegate(this);
25336
25337     this.addEvents({
25338         /**
25339          * @event tabchange
25340          * Fires when the active tab changes
25341          * @param {Roo.TabPanel} this
25342          * @param {Roo.TabPanelItem} activePanel The new active tab
25343          */
25344         "tabchange": true,
25345         /**
25346          * @event beforetabchange
25347          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25348          * @param {Roo.TabPanel} this
25349          * @param {Object} e Set cancel to true on this object to cancel the tab change
25350          * @param {Roo.TabPanelItem} tab The tab being changed to
25351          */
25352         "beforetabchange" : true
25353     });
25354
25355     Roo.EventManager.onWindowResize(this.onResize, this);
25356     this.cpad = this.el.getPadding("lr");
25357     this.hiddenCount = 0;
25358
25359
25360     // toolbar on the tabbar support...
25361     if (this.toolbar) {
25362         var tcfg = this.toolbar;
25363         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25364         this.toolbar = new Roo.Toolbar(tcfg);
25365         if (Roo.isSafari) {
25366             var tbl = tcfg.container.child('table', true);
25367             tbl.setAttribute('width', '100%');
25368         }
25369         
25370     }
25371    
25372
25373
25374     Roo.TabPanel.superclass.constructor.call(this);
25375 };
25376
25377 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25378     /*
25379      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25380      */
25381     tabPosition : "top",
25382     /*
25383      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25384      */
25385     currentTabWidth : 0,
25386     /*
25387      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25388      */
25389     minTabWidth : 40,
25390     /*
25391      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25392      */
25393     maxTabWidth : 250,
25394     /*
25395      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25396      */
25397     preferredTabWidth : 175,
25398     /*
25399      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25400      */
25401     resizeTabs : false,
25402     /*
25403      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25404      */
25405     monitorResize : true,
25406     /*
25407      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25408      */
25409     toolbar : false,
25410
25411     /**
25412      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25413      * @param {String} id The id of the div to use <b>or create</b>
25414      * @param {String} text The text for the tab
25415      * @param {String} content (optional) Content to put in the TabPanelItem body
25416      * @param {Boolean} closable (optional) True to create a close icon on the tab
25417      * @return {Roo.TabPanelItem} The created TabPanelItem
25418      */
25419     addTab : function(id, text, content, closable){
25420         var item = new Roo.TabPanelItem(this, id, text, closable);
25421         this.addTabItem(item);
25422         if(content){
25423             item.setContent(content);
25424         }
25425         return item;
25426     },
25427
25428     /**
25429      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25430      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25431      * @return {Roo.TabPanelItem}
25432      */
25433     getTab : function(id){
25434         return this.items[id];
25435     },
25436
25437     /**
25438      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25439      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25440      */
25441     hideTab : function(id){
25442         var t = this.items[id];
25443         if(!t.isHidden()){
25444            t.setHidden(true);
25445            this.hiddenCount++;
25446            this.autoSizeTabs();
25447         }
25448     },
25449
25450     /**
25451      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25452      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25453      */
25454     unhideTab : function(id){
25455         var t = this.items[id];
25456         if(t.isHidden()){
25457            t.setHidden(false);
25458            this.hiddenCount--;
25459            this.autoSizeTabs();
25460         }
25461     },
25462
25463     /**
25464      * Adds an existing {@link Roo.TabPanelItem}.
25465      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25466      */
25467     addTabItem : function(item){
25468         this.items[item.id] = item;
25469         this.items.push(item);
25470         if(this.resizeTabs){
25471            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25472            this.autoSizeTabs();
25473         }else{
25474             item.autoSize();
25475         }
25476     },
25477
25478     /**
25479      * Removes a {@link Roo.TabPanelItem}.
25480      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25481      */
25482     removeTab : function(id){
25483         var items = this.items;
25484         var tab = items[id];
25485         if(!tab) { return; }
25486         var index = items.indexOf(tab);
25487         if(this.active == tab && items.length > 1){
25488             var newTab = this.getNextAvailable(index);
25489             if(newTab) {
25490                 newTab.activate();
25491             }
25492         }
25493         this.stripEl.dom.removeChild(tab.pnode.dom);
25494         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25495             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25496         }
25497         items.splice(index, 1);
25498         delete this.items[tab.id];
25499         tab.fireEvent("close", tab);
25500         tab.purgeListeners();
25501         this.autoSizeTabs();
25502     },
25503
25504     getNextAvailable : function(start){
25505         var items = this.items;
25506         var index = start;
25507         // look for a next tab that will slide over to
25508         // replace the one being removed
25509         while(index < items.length){
25510             var item = items[++index];
25511             if(item && !item.isHidden()){
25512                 return item;
25513             }
25514         }
25515         // if one isn't found select the previous tab (on the left)
25516         index = start;
25517         while(index >= 0){
25518             var item = items[--index];
25519             if(item && !item.isHidden()){
25520                 return item;
25521             }
25522         }
25523         return null;
25524     },
25525
25526     /**
25527      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25528      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25529      */
25530     disableTab : function(id){
25531         var tab = this.items[id];
25532         if(tab && this.active != tab){
25533             tab.disable();
25534         }
25535     },
25536
25537     /**
25538      * Enables a {@link Roo.TabPanelItem} that is disabled.
25539      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25540      */
25541     enableTab : function(id){
25542         var tab = this.items[id];
25543         tab.enable();
25544     },
25545
25546     /**
25547      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25548      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25549      * @return {Roo.TabPanelItem} The TabPanelItem.
25550      */
25551     activate : function(id){
25552         var tab = this.items[id];
25553         if(!tab){
25554             return null;
25555         }
25556         if(tab == this.active || tab.disabled){
25557             return tab;
25558         }
25559         var e = {};
25560         this.fireEvent("beforetabchange", this, e, tab);
25561         if(e.cancel !== true && !tab.disabled){
25562             if(this.active){
25563                 this.active.hide();
25564             }
25565             this.active = this.items[id];
25566             this.active.show();
25567             this.fireEvent("tabchange", this, this.active);
25568         }
25569         return tab;
25570     },
25571
25572     /**
25573      * Gets the active {@link Roo.TabPanelItem}.
25574      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25575      */
25576     getActiveTab : function(){
25577         return this.active;
25578     },
25579
25580     /**
25581      * Updates the tab body element to fit the height of the container element
25582      * for overflow scrolling
25583      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25584      */
25585     syncHeight : function(targetHeight){
25586         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25587         var bm = this.bodyEl.getMargins();
25588         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25589         this.bodyEl.setHeight(newHeight);
25590         return newHeight;
25591     },
25592
25593     onResize : function(){
25594         if(this.monitorResize){
25595             this.autoSizeTabs();
25596         }
25597     },
25598
25599     /**
25600      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25601      */
25602     beginUpdate : function(){
25603         this.updating = true;
25604     },
25605
25606     /**
25607      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25608      */
25609     endUpdate : function(){
25610         this.updating = false;
25611         this.autoSizeTabs();
25612     },
25613
25614     /**
25615      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25616      */
25617     autoSizeTabs : function(){
25618         var count = this.items.length;
25619         var vcount = count - this.hiddenCount;
25620         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25621         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25622         var availWidth = Math.floor(w / vcount);
25623         var b = this.stripBody;
25624         if(b.getWidth() > w){
25625             var tabs = this.items;
25626             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25627             if(availWidth < this.minTabWidth){
25628                 /*if(!this.sleft){    // incomplete scrolling code
25629                     this.createScrollButtons();
25630                 }
25631                 this.showScroll();
25632                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25633             }
25634         }else{
25635             if(this.currentTabWidth < this.preferredTabWidth){
25636                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25637             }
25638         }
25639     },
25640
25641     /**
25642      * Returns the number of tabs in this TabPanel.
25643      * @return {Number}
25644      */
25645      getCount : function(){
25646          return this.items.length;
25647      },
25648
25649     /**
25650      * Resizes all the tabs to the passed width
25651      * @param {Number} The new width
25652      */
25653     setTabWidth : function(width){
25654         this.currentTabWidth = width;
25655         for(var i = 0, len = this.items.length; i < len; i++) {
25656                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25657         }
25658     },
25659
25660     /**
25661      * Destroys this TabPanel
25662      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25663      */
25664     destroy : function(removeEl){
25665         Roo.EventManager.removeResizeListener(this.onResize, this);
25666         for(var i = 0, len = this.items.length; i < len; i++){
25667             this.items[i].purgeListeners();
25668         }
25669         if(removeEl === true){
25670             this.el.update("");
25671             this.el.remove();
25672         }
25673     }
25674 });
25675
25676 /**
25677  * @class Roo.TabPanelItem
25678  * @extends Roo.util.Observable
25679  * Represents an individual item (tab plus body) in a TabPanel.
25680  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25681  * @param {String} id The id of this TabPanelItem
25682  * @param {String} text The text for the tab of this TabPanelItem
25683  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25684  */
25685 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25686     /**
25687      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25688      * @type Roo.TabPanel
25689      */
25690     this.tabPanel = tabPanel;
25691     /**
25692      * The id for this TabPanelItem
25693      * @type String
25694      */
25695     this.id = id;
25696     /** @private */
25697     this.disabled = false;
25698     /** @private */
25699     this.text = text;
25700     /** @private */
25701     this.loaded = false;
25702     this.closable = closable;
25703
25704     /**
25705      * The body element for this TabPanelItem.
25706      * @type Roo.Element
25707      */
25708     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25709     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25710     this.bodyEl.setStyle("display", "block");
25711     this.bodyEl.setStyle("zoom", "1");
25712     this.hideAction();
25713
25714     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25715     /** @private */
25716     this.el = Roo.get(els.el, true);
25717     this.inner = Roo.get(els.inner, true);
25718     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25719     this.pnode = Roo.get(els.el.parentNode, true);
25720     this.el.on("mousedown", this.onTabMouseDown, this);
25721     this.el.on("click", this.onTabClick, this);
25722     /** @private */
25723     if(closable){
25724         var c = Roo.get(els.close, true);
25725         c.dom.title = this.closeText;
25726         c.addClassOnOver("close-over");
25727         c.on("click", this.closeClick, this);
25728      }
25729
25730     this.addEvents({
25731          /**
25732          * @event activate
25733          * Fires when this tab becomes the active tab.
25734          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25735          * @param {Roo.TabPanelItem} this
25736          */
25737         "activate": true,
25738         /**
25739          * @event beforeclose
25740          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25741          * @param {Roo.TabPanelItem} this
25742          * @param {Object} e Set cancel to true on this object to cancel the close.
25743          */
25744         "beforeclose": true,
25745         /**
25746          * @event close
25747          * Fires when this tab is closed.
25748          * @param {Roo.TabPanelItem} this
25749          */
25750          "close": true,
25751         /**
25752          * @event deactivate
25753          * Fires when this tab is no longer the active tab.
25754          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25755          * @param {Roo.TabPanelItem} this
25756          */
25757          "deactivate" : true
25758     });
25759     this.hidden = false;
25760
25761     Roo.TabPanelItem.superclass.constructor.call(this);
25762 };
25763
25764 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25765     purgeListeners : function(){
25766        Roo.util.Observable.prototype.purgeListeners.call(this);
25767        this.el.removeAllListeners();
25768     },
25769     /**
25770      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25771      */
25772     show : function(){
25773         this.pnode.addClass("on");
25774         this.showAction();
25775         if(Roo.isOpera){
25776             this.tabPanel.stripWrap.repaint();
25777         }
25778         this.fireEvent("activate", this.tabPanel, this);
25779     },
25780
25781     /**
25782      * Returns true if this tab is the active tab.
25783      * @return {Boolean}
25784      */
25785     isActive : function(){
25786         return this.tabPanel.getActiveTab() == this;
25787     },
25788
25789     /**
25790      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25791      */
25792     hide : function(){
25793         this.pnode.removeClass("on");
25794         this.hideAction();
25795         this.fireEvent("deactivate", this.tabPanel, this);
25796     },
25797
25798     hideAction : function(){
25799         this.bodyEl.hide();
25800         this.bodyEl.setStyle("position", "absolute");
25801         this.bodyEl.setLeft("-20000px");
25802         this.bodyEl.setTop("-20000px");
25803     },
25804
25805     showAction : function(){
25806         this.bodyEl.setStyle("position", "relative");
25807         this.bodyEl.setTop("");
25808         this.bodyEl.setLeft("");
25809         this.bodyEl.show();
25810     },
25811
25812     /**
25813      * Set the tooltip for the tab.
25814      * @param {String} tooltip The tab's tooltip
25815      */
25816     setTooltip : function(text){
25817         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25818             this.textEl.dom.qtip = text;
25819             this.textEl.dom.removeAttribute('title');
25820         }else{
25821             this.textEl.dom.title = text;
25822         }
25823     },
25824
25825     onTabClick : function(e){
25826         e.preventDefault();
25827         this.tabPanel.activate(this.id);
25828     },
25829
25830     onTabMouseDown : function(e){
25831         e.preventDefault();
25832         this.tabPanel.activate(this.id);
25833     },
25834
25835     getWidth : function(){
25836         return this.inner.getWidth();
25837     },
25838
25839     setWidth : function(width){
25840         var iwidth = width - this.pnode.getPadding("lr");
25841         this.inner.setWidth(iwidth);
25842         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25843         this.pnode.setWidth(width);
25844     },
25845
25846     /**
25847      * Show or hide the tab
25848      * @param {Boolean} hidden True to hide or false to show.
25849      */
25850     setHidden : function(hidden){
25851         this.hidden = hidden;
25852         this.pnode.setStyle("display", hidden ? "none" : "");
25853     },
25854
25855     /**
25856      * Returns true if this tab is "hidden"
25857      * @return {Boolean}
25858      */
25859     isHidden : function(){
25860         return this.hidden;
25861     },
25862
25863     /**
25864      * Returns the text for this tab
25865      * @return {String}
25866      */
25867     getText : function(){
25868         return this.text;
25869     },
25870
25871     autoSize : function(){
25872         //this.el.beginMeasure();
25873         this.textEl.setWidth(1);
25874         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25875         //this.el.endMeasure();
25876     },
25877
25878     /**
25879      * Sets the text for the tab (Note: this also sets the tooltip text)
25880      * @param {String} text The tab's text and tooltip
25881      */
25882     setText : function(text){
25883         this.text = text;
25884         this.textEl.update(text);
25885         this.setTooltip(text);
25886         if(!this.tabPanel.resizeTabs){
25887             this.autoSize();
25888         }
25889     },
25890     /**
25891      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25892      */
25893     activate : function(){
25894         this.tabPanel.activate(this.id);
25895     },
25896
25897     /**
25898      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25899      */
25900     disable : function(){
25901         if(this.tabPanel.active != this){
25902             this.disabled = true;
25903             this.pnode.addClass("disabled");
25904         }
25905     },
25906
25907     /**
25908      * Enables this TabPanelItem if it was previously disabled.
25909      */
25910     enable : function(){
25911         this.disabled = false;
25912         this.pnode.removeClass("disabled");
25913     },
25914
25915     /**
25916      * Sets the content for this TabPanelItem.
25917      * @param {String} content The content
25918      * @param {Boolean} loadScripts true to look for and load scripts
25919      */
25920     setContent : function(content, loadScripts){
25921         this.bodyEl.update(content, loadScripts);
25922     },
25923
25924     /**
25925      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25926      * @return {Roo.UpdateManager} The UpdateManager
25927      */
25928     getUpdateManager : function(){
25929         return this.bodyEl.getUpdateManager();
25930     },
25931
25932     /**
25933      * Set a URL to be used to load the content for this TabPanelItem.
25934      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25935      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25936      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25937      * @return {Roo.UpdateManager} The UpdateManager
25938      */
25939     setUrl : function(url, params, loadOnce){
25940         if(this.refreshDelegate){
25941             this.un('activate', this.refreshDelegate);
25942         }
25943         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25944         this.on("activate", this.refreshDelegate);
25945         return this.bodyEl.getUpdateManager();
25946     },
25947
25948     /** @private */
25949     _handleRefresh : function(url, params, loadOnce){
25950         if(!loadOnce || !this.loaded){
25951             var updater = this.bodyEl.getUpdateManager();
25952             updater.update(url, params, this._setLoaded.createDelegate(this));
25953         }
25954     },
25955
25956     /**
25957      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25958      *   Will fail silently if the setUrl method has not been called.
25959      *   This does not activate the panel, just updates its content.
25960      */
25961     refresh : function(){
25962         if(this.refreshDelegate){
25963            this.loaded = false;
25964            this.refreshDelegate();
25965         }
25966     },
25967
25968     /** @private */
25969     _setLoaded : function(){
25970         this.loaded = true;
25971     },
25972
25973     /** @private */
25974     closeClick : function(e){
25975         var o = {};
25976         e.stopEvent();
25977         this.fireEvent("beforeclose", this, o);
25978         if(o.cancel !== true){
25979             this.tabPanel.removeTab(this.id);
25980         }
25981     },
25982     /**
25983      * The text displayed in the tooltip for the close icon.
25984      * @type String
25985      */
25986     closeText : "Close this tab"
25987 });
25988
25989 /** @private */
25990 Roo.TabPanel.prototype.createStrip = function(container){
25991     var strip = document.createElement("div");
25992     strip.className = "x-tabs-wrap";
25993     container.appendChild(strip);
25994     return strip;
25995 };
25996 /** @private */
25997 Roo.TabPanel.prototype.createStripList = function(strip){
25998     // div wrapper for retard IE
25999     // returns the "tr" element.
26000     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26001         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26002         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26003     return strip.firstChild.firstChild.firstChild.firstChild;
26004 };
26005 /** @private */
26006 Roo.TabPanel.prototype.createBody = function(container){
26007     var body = document.createElement("div");
26008     Roo.id(body, "tab-body");
26009     Roo.fly(body).addClass("x-tabs-body");
26010     container.appendChild(body);
26011     return body;
26012 };
26013 /** @private */
26014 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26015     var body = Roo.getDom(id);
26016     if(!body){
26017         body = document.createElement("div");
26018         body.id = id;
26019     }
26020     Roo.fly(body).addClass("x-tabs-item-body");
26021     bodyEl.insertBefore(body, bodyEl.firstChild);
26022     return body;
26023 };
26024 /** @private */
26025 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26026     var td = document.createElement("td");
26027     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26028     //stripEl.appendChild(td);
26029     if(closable){
26030         td.className = "x-tabs-closable";
26031         if(!this.closeTpl){
26032             this.closeTpl = new Roo.Template(
26033                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26034                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26035                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26036             );
26037         }
26038         var el = this.closeTpl.overwrite(td, {"text": text});
26039         var close = el.getElementsByTagName("div")[0];
26040         var inner = el.getElementsByTagName("em")[0];
26041         return {"el": el, "close": close, "inner": inner};
26042     } else {
26043         if(!this.tabTpl){
26044             this.tabTpl = new Roo.Template(
26045                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26046                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26047             );
26048         }
26049         var el = this.tabTpl.overwrite(td, {"text": text});
26050         var inner = el.getElementsByTagName("em")[0];
26051         return {"el": el, "inner": inner};
26052     }
26053 };/*
26054  * Based on:
26055  * Ext JS Library 1.1.1
26056  * Copyright(c) 2006-2007, Ext JS, LLC.
26057  *
26058  * Originally Released Under LGPL - original licence link has changed is not relivant.
26059  *
26060  * Fork - LGPL
26061  * <script type="text/javascript">
26062  */
26063
26064 /**
26065  * @class Roo.Button
26066  * @extends Roo.util.Observable
26067  * Simple Button class
26068  * @cfg {String} text The button text
26069  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26070  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26071  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26072  * @cfg {Object} scope The scope of the handler
26073  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26074  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26075  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26076  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26077  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26078  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26079    applies if enableToggle = true)
26080  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26081  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26082   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26083  * @constructor
26084  * Create a new button
26085  * @param {Object} config The config object
26086  */
26087 Roo.Button = function(renderTo, config)
26088 {
26089     if (!config) {
26090         config = renderTo;
26091         renderTo = config.renderTo || false;
26092     }
26093     
26094     Roo.apply(this, config);
26095     this.addEvents({
26096         /**
26097              * @event click
26098              * Fires when this button is clicked
26099              * @param {Button} this
26100              * @param {EventObject} e The click event
26101              */
26102             "click" : true,
26103         /**
26104              * @event toggle
26105              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26106              * @param {Button} this
26107              * @param {Boolean} pressed
26108              */
26109             "toggle" : true,
26110         /**
26111              * @event mouseover
26112              * Fires when the mouse hovers over the button
26113              * @param {Button} this
26114              * @param {Event} e The event object
26115              */
26116         'mouseover' : true,
26117         /**
26118              * @event mouseout
26119              * Fires when the mouse exits the button
26120              * @param {Button} this
26121              * @param {Event} e The event object
26122              */
26123         'mouseout': true,
26124          /**
26125              * @event render
26126              * Fires when the button is rendered
26127              * @param {Button} this
26128              */
26129         'render': true
26130     });
26131     if(this.menu){
26132         this.menu = Roo.menu.MenuMgr.get(this.menu);
26133     }
26134     // register listeners first!!  - so render can be captured..
26135     Roo.util.Observable.call(this);
26136     if(renderTo){
26137         this.render(renderTo);
26138     }
26139     
26140   
26141 };
26142
26143 Roo.extend(Roo.Button, Roo.util.Observable, {
26144     /**
26145      * 
26146      */
26147     
26148     /**
26149      * Read-only. True if this button is hidden
26150      * @type Boolean
26151      */
26152     hidden : false,
26153     /**
26154      * Read-only. True if this button is disabled
26155      * @type Boolean
26156      */
26157     disabled : false,
26158     /**
26159      * Read-only. True if this button is pressed (only if enableToggle = true)
26160      * @type Boolean
26161      */
26162     pressed : false,
26163
26164     /**
26165      * @cfg {Number} tabIndex 
26166      * The DOM tabIndex for this button (defaults to undefined)
26167      */
26168     tabIndex : undefined,
26169
26170     /**
26171      * @cfg {Boolean} enableToggle
26172      * True to enable pressed/not pressed toggling (defaults to false)
26173      */
26174     enableToggle: false,
26175     /**
26176      * @cfg {Mixed} menu
26177      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26178      */
26179     menu : undefined,
26180     /**
26181      * @cfg {String} menuAlign
26182      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26183      */
26184     menuAlign : "tl-bl?",
26185
26186     /**
26187      * @cfg {String} iconCls
26188      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26189      */
26190     iconCls : undefined,
26191     /**
26192      * @cfg {String} type
26193      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26194      */
26195     type : 'button',
26196
26197     // private
26198     menuClassTarget: 'tr',
26199
26200     /**
26201      * @cfg {String} clickEvent
26202      * The type of event to map to the button's event handler (defaults to 'click')
26203      */
26204     clickEvent : 'click',
26205
26206     /**
26207      * @cfg {Boolean} handleMouseEvents
26208      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26209      */
26210     handleMouseEvents : true,
26211
26212     /**
26213      * @cfg {String} tooltipType
26214      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26215      */
26216     tooltipType : 'qtip',
26217
26218     /**
26219      * @cfg {String} cls
26220      * A CSS class to apply to the button's main element.
26221      */
26222     
26223     /**
26224      * @cfg {Roo.Template} template (Optional)
26225      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26226      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26227      * require code modifications if required elements (e.g. a button) aren't present.
26228      */
26229
26230     // private
26231     render : function(renderTo){
26232         var btn;
26233         if(this.hideParent){
26234             this.parentEl = Roo.get(renderTo);
26235         }
26236         if(!this.dhconfig){
26237             if(!this.template){
26238                 if(!Roo.Button.buttonTemplate){
26239                     // hideous table template
26240                     Roo.Button.buttonTemplate = new Roo.Template(
26241                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26242                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
26243                         "</tr></tbody></table>");
26244                 }
26245                 this.template = Roo.Button.buttonTemplate;
26246             }
26247             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26248             var btnEl = btn.child("button:first");
26249             btnEl.on('focus', this.onFocus, this);
26250             btnEl.on('blur', this.onBlur, this);
26251             if(this.cls){
26252                 btn.addClass(this.cls);
26253             }
26254             if(this.icon){
26255                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26256             }
26257             if(this.iconCls){
26258                 btnEl.addClass(this.iconCls);
26259                 if(!this.cls){
26260                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26261                 }
26262             }
26263             if(this.tabIndex !== undefined){
26264                 btnEl.dom.tabIndex = this.tabIndex;
26265             }
26266             if(this.tooltip){
26267                 if(typeof this.tooltip == 'object'){
26268                     Roo.QuickTips.tips(Roo.apply({
26269                           target: btnEl.id
26270                     }, this.tooltip));
26271                 } else {
26272                     btnEl.dom[this.tooltipType] = this.tooltip;
26273                 }
26274             }
26275         }else{
26276             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26277         }
26278         this.el = btn;
26279         if(this.id){
26280             this.el.dom.id = this.el.id = this.id;
26281         }
26282         if(this.menu){
26283             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26284             this.menu.on("show", this.onMenuShow, this);
26285             this.menu.on("hide", this.onMenuHide, this);
26286         }
26287         btn.addClass("x-btn");
26288         if(Roo.isIE && !Roo.isIE7){
26289             this.autoWidth.defer(1, this);
26290         }else{
26291             this.autoWidth();
26292         }
26293         if(this.handleMouseEvents){
26294             btn.on("mouseover", this.onMouseOver, this);
26295             btn.on("mouseout", this.onMouseOut, this);
26296             btn.on("mousedown", this.onMouseDown, this);
26297         }
26298         btn.on(this.clickEvent, this.onClick, this);
26299         //btn.on("mouseup", this.onMouseUp, this);
26300         if(this.hidden){
26301             this.hide();
26302         }
26303         if(this.disabled){
26304             this.disable();
26305         }
26306         Roo.ButtonToggleMgr.register(this);
26307         if(this.pressed){
26308             this.el.addClass("x-btn-pressed");
26309         }
26310         if(this.repeat){
26311             var repeater = new Roo.util.ClickRepeater(btn,
26312                 typeof this.repeat == "object" ? this.repeat : {}
26313             );
26314             repeater.on("click", this.onClick,  this);
26315         }
26316         
26317         this.fireEvent('render', this);
26318         
26319     },
26320     /**
26321      * Returns the button's underlying element
26322      * @return {Roo.Element} The element
26323      */
26324     getEl : function(){
26325         return this.el;  
26326     },
26327     
26328     /**
26329      * Destroys this Button and removes any listeners.
26330      */
26331     destroy : function(){
26332         Roo.ButtonToggleMgr.unregister(this);
26333         this.el.removeAllListeners();
26334         this.purgeListeners();
26335         this.el.remove();
26336     },
26337
26338     // private
26339     autoWidth : function(){
26340         if(this.el){
26341             this.el.setWidth("auto");
26342             if(Roo.isIE7 && Roo.isStrict){
26343                 var ib = this.el.child('button');
26344                 if(ib && ib.getWidth() > 20){
26345                     ib.clip();
26346                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26347                 }
26348             }
26349             if(this.minWidth){
26350                 if(this.hidden){
26351                     this.el.beginMeasure();
26352                 }
26353                 if(this.el.getWidth() < this.minWidth){
26354                     this.el.setWidth(this.minWidth);
26355                 }
26356                 if(this.hidden){
26357                     this.el.endMeasure();
26358                 }
26359             }
26360         }
26361     },
26362
26363     /**
26364      * Assigns this button's click handler
26365      * @param {Function} handler The function to call when the button is clicked
26366      * @param {Object} scope (optional) Scope for the function passed in
26367      */
26368     setHandler : function(handler, scope){
26369         this.handler = handler;
26370         this.scope = scope;  
26371     },
26372     
26373     /**
26374      * Sets this button's text
26375      * @param {String} text The button text
26376      */
26377     setText : function(text){
26378         this.text = text;
26379         if(this.el){
26380             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26381         }
26382         this.autoWidth();
26383     },
26384     
26385     /**
26386      * Gets the text for this button
26387      * @return {String} The button text
26388      */
26389     getText : function(){
26390         return this.text;  
26391     },
26392     
26393     /**
26394      * Show this button
26395      */
26396     show: function(){
26397         this.hidden = false;
26398         if(this.el){
26399             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26400         }
26401     },
26402     
26403     /**
26404      * Hide this button
26405      */
26406     hide: function(){
26407         this.hidden = true;
26408         if(this.el){
26409             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26410         }
26411     },
26412     
26413     /**
26414      * Convenience function for boolean show/hide
26415      * @param {Boolean} visible True to show, false to hide
26416      */
26417     setVisible: function(visible){
26418         if(visible) {
26419             this.show();
26420         }else{
26421             this.hide();
26422         }
26423     },
26424     
26425     /**
26426      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26427      * @param {Boolean} state (optional) Force a particular state
26428      */
26429     toggle : function(state){
26430         state = state === undefined ? !this.pressed : state;
26431         if(state != this.pressed){
26432             if(state){
26433                 this.el.addClass("x-btn-pressed");
26434                 this.pressed = true;
26435                 this.fireEvent("toggle", this, true);
26436             }else{
26437                 this.el.removeClass("x-btn-pressed");
26438                 this.pressed = false;
26439                 this.fireEvent("toggle", this, false);
26440             }
26441             if(this.toggleHandler){
26442                 this.toggleHandler.call(this.scope || this, this, state);
26443             }
26444         }
26445     },
26446     
26447     /**
26448      * Focus the button
26449      */
26450     focus : function(){
26451         this.el.child('button:first').focus();
26452     },
26453     
26454     /**
26455      * Disable this button
26456      */
26457     disable : function(){
26458         if(this.el){
26459             this.el.addClass("x-btn-disabled");
26460         }
26461         this.disabled = true;
26462     },
26463     
26464     /**
26465      * Enable this button
26466      */
26467     enable : function(){
26468         if(this.el){
26469             this.el.removeClass("x-btn-disabled");
26470         }
26471         this.disabled = false;
26472     },
26473
26474     /**
26475      * Convenience function for boolean enable/disable
26476      * @param {Boolean} enabled True to enable, false to disable
26477      */
26478     setDisabled : function(v){
26479         this[v !== true ? "enable" : "disable"]();
26480     },
26481
26482     // private
26483     onClick : function(e){
26484         if(e){
26485             e.preventDefault();
26486         }
26487         if(e.button != 0){
26488             return;
26489         }
26490         if(!this.disabled){
26491             if(this.enableToggle){
26492                 this.toggle();
26493             }
26494             if(this.menu && !this.menu.isVisible()){
26495                 this.menu.show(this.el, this.menuAlign);
26496             }
26497             this.fireEvent("click", this, e);
26498             if(this.handler){
26499                 this.el.removeClass("x-btn-over");
26500                 this.handler.call(this.scope || this, this, e);
26501             }
26502         }
26503     },
26504     // private
26505     onMouseOver : function(e){
26506         if(!this.disabled){
26507             this.el.addClass("x-btn-over");
26508             this.fireEvent('mouseover', this, e);
26509         }
26510     },
26511     // private
26512     onMouseOut : function(e){
26513         if(!e.within(this.el,  true)){
26514             this.el.removeClass("x-btn-over");
26515             this.fireEvent('mouseout', this, e);
26516         }
26517     },
26518     // private
26519     onFocus : function(e){
26520         if(!this.disabled){
26521             this.el.addClass("x-btn-focus");
26522         }
26523     },
26524     // private
26525     onBlur : function(e){
26526         this.el.removeClass("x-btn-focus");
26527     },
26528     // private
26529     onMouseDown : function(e){
26530         if(!this.disabled && e.button == 0){
26531             this.el.addClass("x-btn-click");
26532             Roo.get(document).on('mouseup', this.onMouseUp, this);
26533         }
26534     },
26535     // private
26536     onMouseUp : function(e){
26537         if(e.button == 0){
26538             this.el.removeClass("x-btn-click");
26539             Roo.get(document).un('mouseup', this.onMouseUp, this);
26540         }
26541     },
26542     // private
26543     onMenuShow : function(e){
26544         this.el.addClass("x-btn-menu-active");
26545     },
26546     // private
26547     onMenuHide : function(e){
26548         this.el.removeClass("x-btn-menu-active");
26549     }   
26550 });
26551
26552 // Private utility class used by Button
26553 Roo.ButtonToggleMgr = function(){
26554    var groups = {};
26555    
26556    function toggleGroup(btn, state){
26557        if(state){
26558            var g = groups[btn.toggleGroup];
26559            for(var i = 0, l = g.length; i < l; i++){
26560                if(g[i] != btn){
26561                    g[i].toggle(false);
26562                }
26563            }
26564        }
26565    }
26566    
26567    return {
26568        register : function(btn){
26569            if(!btn.toggleGroup){
26570                return;
26571            }
26572            var g = groups[btn.toggleGroup];
26573            if(!g){
26574                g = groups[btn.toggleGroup] = [];
26575            }
26576            g.push(btn);
26577            btn.on("toggle", toggleGroup);
26578        },
26579        
26580        unregister : function(btn){
26581            if(!btn.toggleGroup){
26582                return;
26583            }
26584            var g = groups[btn.toggleGroup];
26585            if(g){
26586                g.remove(btn);
26587                btn.un("toggle", toggleGroup);
26588            }
26589        }
26590    };
26591 }();/*
26592  * Based on:
26593  * Ext JS Library 1.1.1
26594  * Copyright(c) 2006-2007, Ext JS, LLC.
26595  *
26596  * Originally Released Under LGPL - original licence link has changed is not relivant.
26597  *
26598  * Fork - LGPL
26599  * <script type="text/javascript">
26600  */
26601  
26602 /**
26603  * @class Roo.SplitButton
26604  * @extends Roo.Button
26605  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26606  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26607  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26608  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26609  * @cfg {String} arrowTooltip The title attribute of the arrow
26610  * @constructor
26611  * Create a new menu button
26612  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26613  * @param {Object} config The config object
26614  */
26615 Roo.SplitButton = function(renderTo, config){
26616     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26617     /**
26618      * @event arrowclick
26619      * Fires when this button's arrow is clicked
26620      * @param {SplitButton} this
26621      * @param {EventObject} e The click event
26622      */
26623     this.addEvents({"arrowclick":true});
26624 };
26625
26626 Roo.extend(Roo.SplitButton, Roo.Button, {
26627     render : function(renderTo){
26628         // this is one sweet looking template!
26629         var tpl = new Roo.Template(
26630             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26631             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26632             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26633             "</tbody></table></td><td>",
26634             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26635             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26636             "</tbody></table></td></tr></table>"
26637         );
26638         var btn = tpl.append(renderTo, [this.text, this.type], true);
26639         var btnEl = btn.child("button");
26640         if(this.cls){
26641             btn.addClass(this.cls);
26642         }
26643         if(this.icon){
26644             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26645         }
26646         if(this.iconCls){
26647             btnEl.addClass(this.iconCls);
26648             if(!this.cls){
26649                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26650             }
26651         }
26652         this.el = btn;
26653         if(this.handleMouseEvents){
26654             btn.on("mouseover", this.onMouseOver, this);
26655             btn.on("mouseout", this.onMouseOut, this);
26656             btn.on("mousedown", this.onMouseDown, this);
26657             btn.on("mouseup", this.onMouseUp, this);
26658         }
26659         btn.on(this.clickEvent, this.onClick, this);
26660         if(this.tooltip){
26661             if(typeof this.tooltip == 'object'){
26662                 Roo.QuickTips.tips(Roo.apply({
26663                       target: btnEl.id
26664                 }, this.tooltip));
26665             } else {
26666                 btnEl.dom[this.tooltipType] = this.tooltip;
26667             }
26668         }
26669         if(this.arrowTooltip){
26670             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26671         }
26672         if(this.hidden){
26673             this.hide();
26674         }
26675         if(this.disabled){
26676             this.disable();
26677         }
26678         if(this.pressed){
26679             this.el.addClass("x-btn-pressed");
26680         }
26681         if(Roo.isIE && !Roo.isIE7){
26682             this.autoWidth.defer(1, this);
26683         }else{
26684             this.autoWidth();
26685         }
26686         if(this.menu){
26687             this.menu.on("show", this.onMenuShow, this);
26688             this.menu.on("hide", this.onMenuHide, this);
26689         }
26690         this.fireEvent('render', this);
26691     },
26692
26693     // private
26694     autoWidth : function(){
26695         if(this.el){
26696             var tbl = this.el.child("table:first");
26697             var tbl2 = this.el.child("table:last");
26698             this.el.setWidth("auto");
26699             tbl.setWidth("auto");
26700             if(Roo.isIE7 && Roo.isStrict){
26701                 var ib = this.el.child('button:first');
26702                 if(ib && ib.getWidth() > 20){
26703                     ib.clip();
26704                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26705                 }
26706             }
26707             if(this.minWidth){
26708                 if(this.hidden){
26709                     this.el.beginMeasure();
26710                 }
26711                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26712                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26713                 }
26714                 if(this.hidden){
26715                     this.el.endMeasure();
26716                 }
26717             }
26718             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26719         } 
26720     },
26721     /**
26722      * Sets this button's click handler
26723      * @param {Function} handler The function to call when the button is clicked
26724      * @param {Object} scope (optional) Scope for the function passed above
26725      */
26726     setHandler : function(handler, scope){
26727         this.handler = handler;
26728         this.scope = scope;  
26729     },
26730     
26731     /**
26732      * Sets this button's arrow click handler
26733      * @param {Function} handler The function to call when the arrow is clicked
26734      * @param {Object} scope (optional) Scope for the function passed above
26735      */
26736     setArrowHandler : function(handler, scope){
26737         this.arrowHandler = handler;
26738         this.scope = scope;  
26739     },
26740     
26741     /**
26742      * Focus the button
26743      */
26744     focus : function(){
26745         if(this.el){
26746             this.el.child("button:first").focus();
26747         }
26748     },
26749
26750     // private
26751     onClick : function(e){
26752         e.preventDefault();
26753         if(!this.disabled){
26754             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26755                 if(this.menu && !this.menu.isVisible()){
26756                     this.menu.show(this.el, this.menuAlign);
26757                 }
26758                 this.fireEvent("arrowclick", this, e);
26759                 if(this.arrowHandler){
26760                     this.arrowHandler.call(this.scope || this, this, e);
26761                 }
26762             }else{
26763                 this.fireEvent("click", this, e);
26764                 if(this.handler){
26765                     this.handler.call(this.scope || this, this, e);
26766                 }
26767             }
26768         }
26769     },
26770     // private
26771     onMouseDown : function(e){
26772         if(!this.disabled){
26773             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26774         }
26775     },
26776     // private
26777     onMouseUp : function(e){
26778         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26779     }   
26780 });
26781
26782
26783 // backwards compat
26784 Roo.MenuButton = Roo.SplitButton;/*
26785  * Based on:
26786  * Ext JS Library 1.1.1
26787  * Copyright(c) 2006-2007, Ext JS, LLC.
26788  *
26789  * Originally Released Under LGPL - original licence link has changed is not relivant.
26790  *
26791  * Fork - LGPL
26792  * <script type="text/javascript">
26793  */
26794
26795 /**
26796  * @class Roo.Toolbar
26797  * Basic Toolbar class.
26798  * @constructor
26799  * Creates a new Toolbar
26800  * @param {Object} container The config object
26801  */ 
26802 Roo.Toolbar = function(container, buttons, config)
26803 {
26804     /// old consturctor format still supported..
26805     if(container instanceof Array){ // omit the container for later rendering
26806         buttons = container;
26807         config = buttons;
26808         container = null;
26809     }
26810     if (typeof(container) == 'object' && container.xtype) {
26811         config = container;
26812         container = config.container;
26813         buttons = config.buttons || []; // not really - use items!!
26814     }
26815     var xitems = [];
26816     if (config && config.items) {
26817         xitems = config.items;
26818         delete config.items;
26819     }
26820     Roo.apply(this, config);
26821     this.buttons = buttons;
26822     
26823     if(container){
26824         this.render(container);
26825     }
26826     this.xitems = xitems;
26827     Roo.each(xitems, function(b) {
26828         this.add(b);
26829     }, this);
26830     
26831 };
26832
26833 Roo.Toolbar.prototype = {
26834     /**
26835      * @cfg {Array} items
26836      * array of button configs or elements to add (will be converted to a MixedCollection)
26837      */
26838     
26839     /**
26840      * @cfg {String/HTMLElement/Element} container
26841      * The id or element that will contain the toolbar
26842      */
26843     // private
26844     render : function(ct){
26845         this.el = Roo.get(ct);
26846         if(this.cls){
26847             this.el.addClass(this.cls);
26848         }
26849         // using a table allows for vertical alignment
26850         // 100% width is needed by Safari...
26851         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26852         this.tr = this.el.child("tr", true);
26853         var autoId = 0;
26854         this.items = new Roo.util.MixedCollection(false, function(o){
26855             return o.id || ("item" + (++autoId));
26856         });
26857         if(this.buttons){
26858             this.add.apply(this, this.buttons);
26859             delete this.buttons;
26860         }
26861     },
26862
26863     /**
26864      * Adds element(s) to the toolbar -- this function takes a variable number of 
26865      * arguments of mixed type and adds them to the toolbar.
26866      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26867      * <ul>
26868      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26869      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26870      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26871      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26872      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26873      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26874      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26875      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26876      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26877      * </ul>
26878      * @param {Mixed} arg2
26879      * @param {Mixed} etc.
26880      */
26881     add : function(){
26882         var a = arguments, l = a.length;
26883         for(var i = 0; i < l; i++){
26884             this._add(a[i]);
26885         }
26886     },
26887     // private..
26888     _add : function(el) {
26889         
26890         if (el.xtype) {
26891             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26892         }
26893         
26894         if (el.applyTo){ // some kind of form field
26895             return this.addField(el);
26896         } 
26897         if (el.render){ // some kind of Toolbar.Item
26898             return this.addItem(el);
26899         }
26900         if (typeof el == "string"){ // string
26901             if(el == "separator" || el == "-"){
26902                 return this.addSeparator();
26903             }
26904             if (el == " "){
26905                 return this.addSpacer();
26906             }
26907             if(el == "->"){
26908                 return this.addFill();
26909             }
26910             return this.addText(el);
26911             
26912         }
26913         if(el.tagName){ // element
26914             return this.addElement(el);
26915         }
26916         if(typeof el == "object"){ // must be button config?
26917             return this.addButton(el);
26918         }
26919         // and now what?!?!
26920         return false;
26921         
26922     },
26923     
26924     /**
26925      * Add an Xtype element
26926      * @param {Object} xtype Xtype Object
26927      * @return {Object} created Object
26928      */
26929     addxtype : function(e){
26930         return this.add(e);  
26931     },
26932     
26933     /**
26934      * Returns the Element for this toolbar.
26935      * @return {Roo.Element}
26936      */
26937     getEl : function(){
26938         return this.el;  
26939     },
26940     
26941     /**
26942      * Adds a separator
26943      * @return {Roo.Toolbar.Item} The separator item
26944      */
26945     addSeparator : function(){
26946         return this.addItem(new Roo.Toolbar.Separator());
26947     },
26948
26949     /**
26950      * Adds a spacer element
26951      * @return {Roo.Toolbar.Spacer} The spacer item
26952      */
26953     addSpacer : function(){
26954         return this.addItem(new Roo.Toolbar.Spacer());
26955     },
26956
26957     /**
26958      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26959      * @return {Roo.Toolbar.Fill} The fill item
26960      */
26961     addFill : function(){
26962         return this.addItem(new Roo.Toolbar.Fill());
26963     },
26964
26965     /**
26966      * Adds any standard HTML element to the toolbar
26967      * @param {String/HTMLElement/Element} el The element or id of the element to add
26968      * @return {Roo.Toolbar.Item} The element's item
26969      */
26970     addElement : function(el){
26971         return this.addItem(new Roo.Toolbar.Item(el));
26972     },
26973     /**
26974      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26975      * @type Roo.util.MixedCollection  
26976      */
26977     items : false,
26978      
26979     /**
26980      * Adds any Toolbar.Item or subclass
26981      * @param {Roo.Toolbar.Item} item
26982      * @return {Roo.Toolbar.Item} The item
26983      */
26984     addItem : function(item){
26985         var td = this.nextBlock();
26986         item.render(td);
26987         this.items.add(item);
26988         return item;
26989     },
26990     
26991     /**
26992      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26993      * @param {Object/Array} config A button config or array of configs
26994      * @return {Roo.Toolbar.Button/Array}
26995      */
26996     addButton : function(config){
26997         if(config instanceof Array){
26998             var buttons = [];
26999             for(var i = 0, len = config.length; i < len; i++) {
27000                 buttons.push(this.addButton(config[i]));
27001             }
27002             return buttons;
27003         }
27004         var b = config;
27005         if(!(config instanceof Roo.Toolbar.Button)){
27006             b = config.split ?
27007                 new Roo.Toolbar.SplitButton(config) :
27008                 new Roo.Toolbar.Button(config);
27009         }
27010         var td = this.nextBlock();
27011         b.render(td);
27012         this.items.add(b);
27013         return b;
27014     },
27015     
27016     /**
27017      * Adds text to the toolbar
27018      * @param {String} text The text to add
27019      * @return {Roo.Toolbar.Item} The element's item
27020      */
27021     addText : function(text){
27022         return this.addItem(new Roo.Toolbar.TextItem(text));
27023     },
27024     
27025     /**
27026      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27027      * @param {Number} index The index where the item is to be inserted
27028      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27029      * @return {Roo.Toolbar.Button/Item}
27030      */
27031     insertButton : function(index, item){
27032         if(item instanceof Array){
27033             var buttons = [];
27034             for(var i = 0, len = item.length; i < len; i++) {
27035                buttons.push(this.insertButton(index + i, item[i]));
27036             }
27037             return buttons;
27038         }
27039         if (!(item instanceof Roo.Toolbar.Button)){
27040            item = new Roo.Toolbar.Button(item);
27041         }
27042         var td = document.createElement("td");
27043         this.tr.insertBefore(td, this.tr.childNodes[index]);
27044         item.render(td);
27045         this.items.insert(index, item);
27046         return item;
27047     },
27048     
27049     /**
27050      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27051      * @param {Object} config
27052      * @return {Roo.Toolbar.Item} The element's item
27053      */
27054     addDom : function(config, returnEl){
27055         var td = this.nextBlock();
27056         Roo.DomHelper.overwrite(td, config);
27057         var ti = new Roo.Toolbar.Item(td.firstChild);
27058         ti.render(td);
27059         this.items.add(ti);
27060         return ti;
27061     },
27062
27063     /**
27064      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27065      * @type Roo.util.MixedCollection  
27066      */
27067     fields : false,
27068     
27069     /**
27070      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27071      * Note: the field should not have been rendered yet. For a field that has already been
27072      * rendered, use {@link #addElement}.
27073      * @param {Roo.form.Field} field
27074      * @return {Roo.ToolbarItem}
27075      */
27076      
27077       
27078     addField : function(field) {
27079         if (!this.fields) {
27080             var autoId = 0;
27081             this.fields = new Roo.util.MixedCollection(false, function(o){
27082                 return o.id || ("item" + (++autoId));
27083             });
27084
27085         }
27086         
27087         var td = this.nextBlock();
27088         field.render(td);
27089         var ti = new Roo.Toolbar.Item(td.firstChild);
27090         ti.render(td);
27091         this.items.add(ti);
27092         this.fields.add(field);
27093         return ti;
27094     },
27095     /**
27096      * Hide the toolbar
27097      * @method hide
27098      */
27099      
27100       
27101     hide : function()
27102     {
27103         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27104         this.el.child('div').hide();
27105     },
27106     /**
27107      * Show the toolbar
27108      * @method show
27109      */
27110     show : function()
27111     {
27112         this.el.child('div').show();
27113     },
27114       
27115     // private
27116     nextBlock : function(){
27117         var td = document.createElement("td");
27118         this.tr.appendChild(td);
27119         return td;
27120     },
27121
27122     // private
27123     destroy : function(){
27124         if(this.items){ // rendered?
27125             Roo.destroy.apply(Roo, this.items.items);
27126         }
27127         if(this.fields){ // rendered?
27128             Roo.destroy.apply(Roo, this.fields.items);
27129         }
27130         Roo.Element.uncache(this.el, this.tr);
27131     }
27132 };
27133
27134 /**
27135  * @class Roo.Toolbar.Item
27136  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27137  * @constructor
27138  * Creates a new Item
27139  * @param {HTMLElement} el 
27140  */
27141 Roo.Toolbar.Item = function(el){
27142     this.el = Roo.getDom(el);
27143     this.id = Roo.id(this.el);
27144     this.hidden = false;
27145 };
27146
27147 Roo.Toolbar.Item.prototype = {
27148     
27149     /**
27150      * Get this item's HTML Element
27151      * @return {HTMLElement}
27152      */
27153     getEl : function(){
27154        return this.el;  
27155     },
27156
27157     // private
27158     render : function(td){
27159         this.td = td;
27160         td.appendChild(this.el);
27161     },
27162     
27163     /**
27164      * Removes and destroys this item.
27165      */
27166     destroy : function(){
27167         this.td.parentNode.removeChild(this.td);
27168     },
27169     
27170     /**
27171      * Shows this item.
27172      */
27173     show: function(){
27174         this.hidden = false;
27175         this.td.style.display = "";
27176     },
27177     
27178     /**
27179      * Hides this item.
27180      */
27181     hide: function(){
27182         this.hidden = true;
27183         this.td.style.display = "none";
27184     },
27185     
27186     /**
27187      * Convenience function for boolean show/hide.
27188      * @param {Boolean} visible true to show/false to hide
27189      */
27190     setVisible: function(visible){
27191         if(visible) {
27192             this.show();
27193         }else{
27194             this.hide();
27195         }
27196     },
27197     
27198     /**
27199      * Try to focus this item.
27200      */
27201     focus : function(){
27202         Roo.fly(this.el).focus();
27203     },
27204     
27205     /**
27206      * Disables this item.
27207      */
27208     disable : function(){
27209         Roo.fly(this.td).addClass("x-item-disabled");
27210         this.disabled = true;
27211         this.el.disabled = true;
27212     },
27213     
27214     /**
27215      * Enables this item.
27216      */
27217     enable : function(){
27218         Roo.fly(this.td).removeClass("x-item-disabled");
27219         this.disabled = false;
27220         this.el.disabled = false;
27221     }
27222 };
27223
27224
27225 /**
27226  * @class Roo.Toolbar.Separator
27227  * @extends Roo.Toolbar.Item
27228  * A simple toolbar separator class
27229  * @constructor
27230  * Creates a new Separator
27231  */
27232 Roo.Toolbar.Separator = function(){
27233     var s = document.createElement("span");
27234     s.className = "ytb-sep";
27235     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27236 };
27237 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27238     enable:Roo.emptyFn,
27239     disable:Roo.emptyFn,
27240     focus:Roo.emptyFn
27241 });
27242
27243 /**
27244  * @class Roo.Toolbar.Spacer
27245  * @extends Roo.Toolbar.Item
27246  * A simple element that adds extra horizontal space to a toolbar.
27247  * @constructor
27248  * Creates a new Spacer
27249  */
27250 Roo.Toolbar.Spacer = function(){
27251     var s = document.createElement("div");
27252     s.className = "ytb-spacer";
27253     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27254 };
27255 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27256     enable:Roo.emptyFn,
27257     disable:Roo.emptyFn,
27258     focus:Roo.emptyFn
27259 });
27260
27261 /**
27262  * @class Roo.Toolbar.Fill
27263  * @extends Roo.Toolbar.Spacer
27264  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27265  * @constructor
27266  * Creates a new Spacer
27267  */
27268 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27269     // private
27270     render : function(td){
27271         td.style.width = '100%';
27272         Roo.Toolbar.Fill.superclass.render.call(this, td);
27273     }
27274 });
27275
27276 /**
27277  * @class Roo.Toolbar.TextItem
27278  * @extends Roo.Toolbar.Item
27279  * A simple class that renders text directly into a toolbar.
27280  * @constructor
27281  * Creates a new TextItem
27282  * @param {String} text
27283  */
27284 Roo.Toolbar.TextItem = function(text){
27285     if (typeof(text) == 'object') {
27286         text = text.text;
27287     }
27288     var s = document.createElement("span");
27289     s.className = "ytb-text";
27290     s.innerHTML = text;
27291     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27292 };
27293 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27294     enable:Roo.emptyFn,
27295     disable:Roo.emptyFn,
27296     focus:Roo.emptyFn
27297 });
27298
27299 /**
27300  * @class Roo.Toolbar.Button
27301  * @extends Roo.Button
27302  * A button that renders into a toolbar.
27303  * @constructor
27304  * Creates a new Button
27305  * @param {Object} config A standard {@link Roo.Button} config object
27306  */
27307 Roo.Toolbar.Button = function(config){
27308     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27309 };
27310 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27311     render : function(td){
27312         this.td = td;
27313         Roo.Toolbar.Button.superclass.render.call(this, td);
27314     },
27315     
27316     /**
27317      * Removes and destroys this button
27318      */
27319     destroy : function(){
27320         Roo.Toolbar.Button.superclass.destroy.call(this);
27321         this.td.parentNode.removeChild(this.td);
27322     },
27323     
27324     /**
27325      * Shows this button
27326      */
27327     show: function(){
27328         this.hidden = false;
27329         this.td.style.display = "";
27330     },
27331     
27332     /**
27333      * Hides this button
27334      */
27335     hide: function(){
27336         this.hidden = true;
27337         this.td.style.display = "none";
27338     },
27339
27340     /**
27341      * Disables this item
27342      */
27343     disable : function(){
27344         Roo.fly(this.td).addClass("x-item-disabled");
27345         this.disabled = true;
27346     },
27347
27348     /**
27349      * Enables this item
27350      */
27351     enable : function(){
27352         Roo.fly(this.td).removeClass("x-item-disabled");
27353         this.disabled = false;
27354     }
27355 });
27356 // backwards compat
27357 Roo.ToolbarButton = Roo.Toolbar.Button;
27358
27359 /**
27360  * @class Roo.Toolbar.SplitButton
27361  * @extends Roo.SplitButton
27362  * A menu button that renders into a toolbar.
27363  * @constructor
27364  * Creates a new SplitButton
27365  * @param {Object} config A standard {@link Roo.SplitButton} config object
27366  */
27367 Roo.Toolbar.SplitButton = function(config){
27368     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27369 };
27370 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27371     render : function(td){
27372         this.td = td;
27373         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27374     },
27375     
27376     /**
27377      * Removes and destroys this button
27378      */
27379     destroy : function(){
27380         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27381         this.td.parentNode.removeChild(this.td);
27382     },
27383     
27384     /**
27385      * Shows this button
27386      */
27387     show: function(){
27388         this.hidden = false;
27389         this.td.style.display = "";
27390     },
27391     
27392     /**
27393      * Hides this button
27394      */
27395     hide: function(){
27396         this.hidden = true;
27397         this.td.style.display = "none";
27398     }
27399 });
27400
27401 // backwards compat
27402 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27403  * Based on:
27404  * Ext JS Library 1.1.1
27405  * Copyright(c) 2006-2007, Ext JS, LLC.
27406  *
27407  * Originally Released Under LGPL - original licence link has changed is not relivant.
27408  *
27409  * Fork - LGPL
27410  * <script type="text/javascript">
27411  */
27412  
27413 /**
27414  * @class Roo.PagingToolbar
27415  * @extends Roo.Toolbar
27416  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27417  * @constructor
27418  * Create a new PagingToolbar
27419  * @param {Object} config The config object
27420  */
27421 Roo.PagingToolbar = function(el, ds, config)
27422 {
27423     // old args format still supported... - xtype is prefered..
27424     if (typeof(el) == 'object' && el.xtype) {
27425         // created from xtype...
27426         config = el;
27427         ds = el.dataSource;
27428         el = config.container;
27429     }
27430     var items = [];
27431     if (config.items) {
27432         items = config.items;
27433         config.items = [];
27434     }
27435     
27436     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27437     this.ds = ds;
27438     this.cursor = 0;
27439     this.renderButtons(this.el);
27440     this.bind(ds);
27441     
27442     // supprot items array.
27443    
27444     Roo.each(items, function(e) {
27445         this.add(Roo.factory(e));
27446     },this);
27447     
27448 };
27449
27450 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27451     /**
27452      * @cfg {Roo.data.Store} dataSource
27453      * The underlying data store providing the paged data
27454      */
27455     /**
27456      * @cfg {String/HTMLElement/Element} container
27457      * container The id or element that will contain the toolbar
27458      */
27459     /**
27460      * @cfg {Boolean} displayInfo
27461      * True to display the displayMsg (defaults to false)
27462      */
27463     /**
27464      * @cfg {Number} pageSize
27465      * The number of records to display per page (defaults to 20)
27466      */
27467     pageSize: 20,
27468     /**
27469      * @cfg {String} displayMsg
27470      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27471      */
27472     displayMsg : 'Displaying {0} - {1} of {2}',
27473     /**
27474      * @cfg {String} emptyMsg
27475      * The message to display when no records are found (defaults to "No data to display")
27476      */
27477     emptyMsg : 'No data to display',
27478     /**
27479      * Customizable piece of the default paging text (defaults to "Page")
27480      * @type String
27481      */
27482     beforePageText : "Page",
27483     /**
27484      * Customizable piece of the default paging text (defaults to "of %0")
27485      * @type String
27486      */
27487     afterPageText : "of {0}",
27488     /**
27489      * Customizable piece of the default paging text (defaults to "First Page")
27490      * @type String
27491      */
27492     firstText : "First Page",
27493     /**
27494      * Customizable piece of the default paging text (defaults to "Previous Page")
27495      * @type String
27496      */
27497     prevText : "Previous Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "Next Page")
27500      * @type String
27501      */
27502     nextText : "Next Page",
27503     /**
27504      * Customizable piece of the default paging text (defaults to "Last Page")
27505      * @type String
27506      */
27507     lastText : "Last Page",
27508     /**
27509      * Customizable piece of the default paging text (defaults to "Refresh")
27510      * @type String
27511      */
27512     refreshText : "Refresh",
27513
27514     // private
27515     renderButtons : function(el){
27516         Roo.PagingToolbar.superclass.render.call(this, el);
27517         this.first = this.addButton({
27518             tooltip: this.firstText,
27519             cls: "x-btn-icon x-grid-page-first",
27520             disabled: true,
27521             handler: this.onClick.createDelegate(this, ["first"])
27522         });
27523         this.prev = this.addButton({
27524             tooltip: this.prevText,
27525             cls: "x-btn-icon x-grid-page-prev",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["prev"])
27528         });
27529         //this.addSeparator();
27530         this.add(this.beforePageText);
27531         this.field = Roo.get(this.addDom({
27532            tag: "input",
27533            type: "text",
27534            size: "3",
27535            value: "1",
27536            cls: "x-grid-page-number"
27537         }).el);
27538         this.field.on("keydown", this.onPagingKeydown, this);
27539         this.field.on("focus", function(){this.dom.select();});
27540         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27541         this.field.setHeight(18);
27542         //this.addSeparator();
27543         this.next = this.addButton({
27544             tooltip: this.nextText,
27545             cls: "x-btn-icon x-grid-page-next",
27546             disabled: true,
27547             handler: this.onClick.createDelegate(this, ["next"])
27548         });
27549         this.last = this.addButton({
27550             tooltip: this.lastText,
27551             cls: "x-btn-icon x-grid-page-last",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["last"])
27554         });
27555         //this.addSeparator();
27556         this.loading = this.addButton({
27557             tooltip: this.refreshText,
27558             cls: "x-btn-icon x-grid-loading",
27559             handler: this.onClick.createDelegate(this, ["refresh"])
27560         });
27561
27562         if(this.displayInfo){
27563             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27564         }
27565     },
27566
27567     // private
27568     updateInfo : function(){
27569         if(this.displayEl){
27570             var count = this.ds.getCount();
27571             var msg = count == 0 ?
27572                 this.emptyMsg :
27573                 String.format(
27574                     this.displayMsg,
27575                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27576                 );
27577             this.displayEl.update(msg);
27578         }
27579     },
27580
27581     // private
27582     onLoad : function(ds, r, o){
27583        this.cursor = o.params ? o.params.start : 0;
27584        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27585
27586        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27587        this.field.dom.value = ap;
27588        this.first.setDisabled(ap == 1);
27589        this.prev.setDisabled(ap == 1);
27590        this.next.setDisabled(ap == ps);
27591        this.last.setDisabled(ap == ps);
27592        this.loading.enable();
27593        this.updateInfo();
27594     },
27595
27596     // private
27597     getPageData : function(){
27598         var total = this.ds.getTotalCount();
27599         return {
27600             total : total,
27601             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27602             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27603         };
27604     },
27605
27606     // private
27607     onLoadError : function(){
27608         this.loading.enable();
27609     },
27610
27611     // private
27612     onPagingKeydown : function(e){
27613         var k = e.getKey();
27614         var d = this.getPageData();
27615         if(k == e.RETURN){
27616             var v = this.field.dom.value, pageNum;
27617             if(!v || isNaN(pageNum = parseInt(v, 10))){
27618                 this.field.dom.value = d.activePage;
27619                 return;
27620             }
27621             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27623             e.stopEvent();
27624         }
27625         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27626         {
27627           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27628           this.field.dom.value = pageNum;
27629           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27630           e.stopEvent();
27631         }
27632         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27633         {
27634           var v = this.field.dom.value, pageNum; 
27635           var increment = (e.shiftKey) ? 10 : 1;
27636           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27637             increment *= -1;
27638           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27639             this.field.dom.value = d.activePage;
27640             return;
27641           }
27642           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27643           {
27644             this.field.dom.value = parseInt(v, 10) + increment;
27645             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27646             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27647           }
27648           e.stopEvent();
27649         }
27650     },
27651
27652     // private
27653     beforeLoad : function(){
27654         if(this.loading){
27655             this.loading.disable();
27656         }
27657     },
27658
27659     // private
27660     onClick : function(which){
27661         var ds = this.ds;
27662         switch(which){
27663             case "first":
27664                 ds.load({params:{start: 0, limit: this.pageSize}});
27665             break;
27666             case "prev":
27667                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27668             break;
27669             case "next":
27670                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27671             break;
27672             case "last":
27673                 var total = ds.getTotalCount();
27674                 var extra = total % this.pageSize;
27675                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27676                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27677             break;
27678             case "refresh":
27679                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27680             break;
27681         }
27682     },
27683
27684     /**
27685      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27686      * @param {Roo.data.Store} store The data store to unbind
27687      */
27688     unbind : function(ds){
27689         ds.un("beforeload", this.beforeLoad, this);
27690         ds.un("load", this.onLoad, this);
27691         ds.un("loadexception", this.onLoadError, this);
27692         ds.un("remove", this.updateInfo, this);
27693         ds.un("add", this.updateInfo, this);
27694         this.ds = undefined;
27695     },
27696
27697     /**
27698      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27699      * @param {Roo.data.Store} store The data store to bind
27700      */
27701     bind : function(ds){
27702         ds.on("beforeload", this.beforeLoad, this);
27703         ds.on("load", this.onLoad, this);
27704         ds.on("loadexception", this.onLoadError, this);
27705         ds.on("remove", this.updateInfo, this);
27706         ds.on("add", this.updateInfo, this);
27707         this.ds = ds;
27708     }
27709 });/*
27710  * Based on:
27711  * Ext JS Library 1.1.1
27712  * Copyright(c) 2006-2007, Ext JS, LLC.
27713  *
27714  * Originally Released Under LGPL - original licence link has changed is not relivant.
27715  *
27716  * Fork - LGPL
27717  * <script type="text/javascript">
27718  */
27719
27720 /**
27721  * @class Roo.Resizable
27722  * @extends Roo.util.Observable
27723  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27724  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27725  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27726  * the element will be wrapped for you automatically.</p>
27727  * <p>Here is the list of valid resize handles:</p>
27728  * <pre>
27729 Value   Description
27730 ------  -------------------
27731  'n'     north
27732  's'     south
27733  'e'     east
27734  'w'     west
27735  'nw'    northwest
27736  'sw'    southwest
27737  'se'    southeast
27738  'ne'    northeast
27739  'hd'    horizontal drag
27740  'all'   all
27741 </pre>
27742  * <p>Here's an example showing the creation of a typical Resizable:</p>
27743  * <pre><code>
27744 var resizer = new Roo.Resizable("element-id", {
27745     handles: 'all',
27746     minWidth: 200,
27747     minHeight: 100,
27748     maxWidth: 500,
27749     maxHeight: 400,
27750     pinned: true
27751 });
27752 resizer.on("resize", myHandler);
27753 </code></pre>
27754  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27755  * resizer.east.setDisplayed(false);</p>
27756  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27757  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27758  * resize operation's new size (defaults to [0, 0])
27759  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27760  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27761  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27762  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27763  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27764  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27765  * @cfg {Number} width The width of the element in pixels (defaults to null)
27766  * @cfg {Number} height The height of the element in pixels (defaults to null)
27767  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27768  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27769  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27770  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27771  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27772  * in favor of the handles config option (defaults to false)
27773  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27774  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27775  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27776  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27777  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27778  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27779  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27780  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27781  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27782  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27783  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27784  * @constructor
27785  * Create a new resizable component
27786  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27787  * @param {Object} config configuration options
27788   */
27789 Roo.Resizable = function(el, config)
27790 {
27791     this.el = Roo.get(el);
27792
27793     if(config && config.wrap){
27794         config.resizeChild = this.el;
27795         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27796         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27797         this.el.setStyle("overflow", "hidden");
27798         this.el.setPositioning(config.resizeChild.getPositioning());
27799         config.resizeChild.clearPositioning();
27800         if(!config.width || !config.height){
27801             var csize = config.resizeChild.getSize();
27802             this.el.setSize(csize.width, csize.height);
27803         }
27804         if(config.pinned && !config.adjustments){
27805             config.adjustments = "auto";
27806         }
27807     }
27808
27809     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27810     this.proxy.unselectable();
27811     this.proxy.enableDisplayMode('block');
27812
27813     Roo.apply(this, config);
27814
27815     if(this.pinned){
27816         this.disableTrackOver = true;
27817         this.el.addClass("x-resizable-pinned");
27818     }
27819     // if the element isn't positioned, make it relative
27820     var position = this.el.getStyle("position");
27821     if(position != "absolute" && position != "fixed"){
27822         this.el.setStyle("position", "relative");
27823     }
27824     if(!this.handles){ // no handles passed, must be legacy style
27825         this.handles = 's,e,se';
27826         if(this.multiDirectional){
27827             this.handles += ',n,w';
27828         }
27829     }
27830     if(this.handles == "all"){
27831         this.handles = "n s e w ne nw se sw";
27832     }
27833     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27834     var ps = Roo.Resizable.positions;
27835     for(var i = 0, len = hs.length; i < len; i++){
27836         if(hs[i] && ps[hs[i]]){
27837             var pos = ps[hs[i]];
27838             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27839         }
27840     }
27841     // legacy
27842     this.corner = this.southeast;
27843     
27844     // updateBox = the box can move..
27845     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27846         this.updateBox = true;
27847     }
27848
27849     this.activeHandle = null;
27850
27851     if(this.resizeChild){
27852         if(typeof this.resizeChild == "boolean"){
27853             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27854         }else{
27855             this.resizeChild = Roo.get(this.resizeChild, true);
27856         }
27857     }
27858     
27859     if(this.adjustments == "auto"){
27860         var rc = this.resizeChild;
27861         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27862         if(rc && (hw || hn)){
27863             rc.position("relative");
27864             rc.setLeft(hw ? hw.el.getWidth() : 0);
27865             rc.setTop(hn ? hn.el.getHeight() : 0);
27866         }
27867         this.adjustments = [
27868             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27869             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27870         ];
27871     }
27872
27873     if(this.draggable){
27874         this.dd = this.dynamic ?
27875             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27876         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27877     }
27878
27879     // public events
27880     this.addEvents({
27881         /**
27882          * @event beforeresize
27883          * Fired before resize is allowed. Set enabled to false to cancel resize.
27884          * @param {Roo.Resizable} this
27885          * @param {Roo.EventObject} e The mousedown event
27886          */
27887         "beforeresize" : true,
27888         /**
27889          * @event resize
27890          * Fired after a resize.
27891          * @param {Roo.Resizable} this
27892          * @param {Number} width The new width
27893          * @param {Number} height The new height
27894          * @param {Roo.EventObject} e The mouseup event
27895          */
27896         "resize" : true
27897     });
27898
27899     if(this.width !== null && this.height !== null){
27900         this.resizeTo(this.width, this.height);
27901     }else{
27902         this.updateChildSize();
27903     }
27904     if(Roo.isIE){
27905         this.el.dom.style.zoom = 1;
27906     }
27907     Roo.Resizable.superclass.constructor.call(this);
27908 };
27909
27910 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27911         resizeChild : false,
27912         adjustments : [0, 0],
27913         minWidth : 5,
27914         minHeight : 5,
27915         maxWidth : 10000,
27916         maxHeight : 10000,
27917         enabled : true,
27918         animate : false,
27919         duration : .35,
27920         dynamic : false,
27921         handles : false,
27922         multiDirectional : false,
27923         disableTrackOver : false,
27924         easing : 'easeOutStrong',
27925         widthIncrement : 0,
27926         heightIncrement : 0,
27927         pinned : false,
27928         width : null,
27929         height : null,
27930         preserveRatio : false,
27931         transparent: false,
27932         minX: 0,
27933         minY: 0,
27934         draggable: false,
27935
27936         /**
27937          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27938          */
27939         constrainTo: undefined,
27940         /**
27941          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27942          */
27943         resizeRegion: undefined,
27944
27945
27946     /**
27947      * Perform a manual resize
27948      * @param {Number} width
27949      * @param {Number} height
27950      */
27951     resizeTo : function(width, height){
27952         this.el.setSize(width, height);
27953         this.updateChildSize();
27954         this.fireEvent("resize", this, width, height, null);
27955     },
27956
27957     // private
27958     startSizing : function(e, handle){
27959         this.fireEvent("beforeresize", this, e);
27960         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27961
27962             if(!this.overlay){
27963                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27964                 this.overlay.unselectable();
27965                 this.overlay.enableDisplayMode("block");
27966                 this.overlay.on("mousemove", this.onMouseMove, this);
27967                 this.overlay.on("mouseup", this.onMouseUp, this);
27968             }
27969             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27970
27971             this.resizing = true;
27972             this.startBox = this.el.getBox();
27973             this.startPoint = e.getXY();
27974             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27975                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27976
27977             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27978             this.overlay.show();
27979
27980             if(this.constrainTo) {
27981                 var ct = Roo.get(this.constrainTo);
27982                 this.resizeRegion = ct.getRegion().adjust(
27983                     ct.getFrameWidth('t'),
27984                     ct.getFrameWidth('l'),
27985                     -ct.getFrameWidth('b'),
27986                     -ct.getFrameWidth('r')
27987                 );
27988             }
27989
27990             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27991             this.proxy.show();
27992             this.proxy.setBox(this.startBox);
27993             if(!this.dynamic){
27994                 this.proxy.setStyle('visibility', 'visible');
27995             }
27996         }
27997     },
27998
27999     // private
28000     onMouseDown : function(handle, e){
28001         if(this.enabled){
28002             e.stopEvent();
28003             this.activeHandle = handle;
28004             this.startSizing(e, handle);
28005         }
28006     },
28007
28008     // private
28009     onMouseUp : function(e){
28010         var size = this.resizeElement();
28011         this.resizing = false;
28012         this.handleOut();
28013         this.overlay.hide();
28014         this.proxy.hide();
28015         this.fireEvent("resize", this, size.width, size.height, e);
28016     },
28017
28018     // private
28019     updateChildSize : function(){
28020         if(this.resizeChild){
28021             var el = this.el;
28022             var child = this.resizeChild;
28023             var adj = this.adjustments;
28024             if(el.dom.offsetWidth){
28025                 var b = el.getSize(true);
28026                 child.setSize(b.width+adj[0], b.height+adj[1]);
28027             }
28028             // Second call here for IE
28029             // The first call enables instant resizing and
28030             // the second call corrects scroll bars if they
28031             // exist
28032             if(Roo.isIE){
28033                 setTimeout(function(){
28034                     if(el.dom.offsetWidth){
28035                         var b = el.getSize(true);
28036                         child.setSize(b.width+adj[0], b.height+adj[1]);
28037                     }
28038                 }, 10);
28039             }
28040         }
28041     },
28042
28043     // private
28044     snap : function(value, inc, min){
28045         if(!inc || !value) return value;
28046         var newValue = value;
28047         var m = value % inc;
28048         if(m > 0){
28049             if(m > (inc/2)){
28050                 newValue = value + (inc-m);
28051             }else{
28052                 newValue = value - m;
28053             }
28054         }
28055         return Math.max(min, newValue);
28056     },
28057
28058     // private
28059     resizeElement : function(){
28060         var box = this.proxy.getBox();
28061         if(this.updateBox){
28062             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28063         }else{
28064             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28065         }
28066         this.updateChildSize();
28067         if(!this.dynamic){
28068             this.proxy.hide();
28069         }
28070         return box;
28071     },
28072
28073     // private
28074     constrain : function(v, diff, m, mx){
28075         if(v - diff < m){
28076             diff = v - m;
28077         }else if(v - diff > mx){
28078             diff = mx - v;
28079         }
28080         return diff;
28081     },
28082
28083     // private
28084     onMouseMove : function(e){
28085         if(this.enabled){
28086             try{// try catch so if something goes wrong the user doesn't get hung
28087
28088             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28089                 return;
28090             }
28091
28092             //var curXY = this.startPoint;
28093             var curSize = this.curSize || this.startBox;
28094             var x = this.startBox.x, y = this.startBox.y;
28095             var ox = x, oy = y;
28096             var w = curSize.width, h = curSize.height;
28097             var ow = w, oh = h;
28098             var mw = this.minWidth, mh = this.minHeight;
28099             var mxw = this.maxWidth, mxh = this.maxHeight;
28100             var wi = this.widthIncrement;
28101             var hi = this.heightIncrement;
28102
28103             var eventXY = e.getXY();
28104             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28105             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28106
28107             var pos = this.activeHandle.position;
28108
28109             switch(pos){
28110                 case "east":
28111                     w += diffX;
28112                     w = Math.min(Math.max(mw, w), mxw);
28113                     break;
28114              
28115                 case "south":
28116                     h += diffY;
28117                     h = Math.min(Math.max(mh, h), mxh);
28118                     break;
28119                 case "southeast":
28120                     w += diffX;
28121                     h += diffY;
28122                     w = Math.min(Math.max(mw, w), mxw);
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "north":
28126                     diffY = this.constrain(h, diffY, mh, mxh);
28127                     y += diffY;
28128                     h -= diffY;
28129                     break;
28130                 case "hdrag":
28131                     
28132                     if (wi) {
28133                         var adiffX = Math.abs(diffX);
28134                         var sub = (adiffX % wi); // how much 
28135                         if (sub > (wi/2)) { // far enough to snap
28136                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28137                         } else {
28138                             // remove difference.. 
28139                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28140                         }
28141                     }
28142                     x += diffX;
28143                     x = Math.max(this.minX, x);
28144                     break;
28145                 case "west":
28146                     diffX = this.constrain(w, diffX, mw, mxw);
28147                     x += diffX;
28148                     w -= diffX;
28149                     break;
28150                 case "northeast":
28151                     w += diffX;
28152                     w = Math.min(Math.max(mw, w), mxw);
28153                     diffY = this.constrain(h, diffY, mh, mxh);
28154                     y += diffY;
28155                     h -= diffY;
28156                     break;
28157                 case "northwest":
28158                     diffX = this.constrain(w, diffX, mw, mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     x += diffX;
28163                     w -= diffX;
28164                     break;
28165                case "southwest":
28166                     diffX = this.constrain(w, diffX, mw, mxw);
28167                     h += diffY;
28168                     h = Math.min(Math.max(mh, h), mxh);
28169                     x += diffX;
28170                     w -= diffX;
28171                     break;
28172             }
28173
28174             var sw = this.snap(w, wi, mw);
28175             var sh = this.snap(h, hi, mh);
28176             if(sw != w || sh != h){
28177                 switch(pos){
28178                     case "northeast":
28179                         y -= sh - h;
28180                     break;
28181                     case "north":
28182                         y -= sh - h;
28183                         break;
28184                     case "southwest":
28185                         x -= sw - w;
28186                     break;
28187                     case "west":
28188                         x -= sw - w;
28189                         break;
28190                     case "northwest":
28191                         x -= sw - w;
28192                         y -= sh - h;
28193                     break;
28194                 }
28195                 w = sw;
28196                 h = sh;
28197             }
28198
28199             if(this.preserveRatio){
28200                 switch(pos){
28201                     case "southeast":
28202                     case "east":
28203                         h = oh * (w/ow);
28204                         h = Math.min(Math.max(mh, h), mxh);
28205                         w = ow * (h/oh);
28206                        break;
28207                     case "south":
28208                         w = ow * (h/oh);
28209                         w = Math.min(Math.max(mw, w), mxw);
28210                         h = oh * (w/ow);
28211                         break;
28212                     case "northeast":
28213                         w = ow * (h/oh);
28214                         w = Math.min(Math.max(mw, w), mxw);
28215                         h = oh * (w/ow);
28216                     break;
28217                     case "north":
28218                         var tw = w;
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                         x += (tw - w) / 2;
28223                         break;
28224                     case "southwest":
28225                         h = oh * (w/ow);
28226                         h = Math.min(Math.max(mh, h), mxh);
28227                         var tw = w;
28228                         w = ow * (h/oh);
28229                         x += tw - w;
28230                         break;
28231                     case "west":
28232                         var th = h;
28233                         h = oh * (w/ow);
28234                         h = Math.min(Math.max(mh, h), mxh);
28235                         y += (th - h) / 2;
28236                         var tw = w;
28237                         w = ow * (h/oh);
28238                         x += tw - w;
28239                        break;
28240                     case "northwest":
28241                         var tw = w;
28242                         var th = h;
28243                         h = oh * (w/ow);
28244                         h = Math.min(Math.max(mh, h), mxh);
28245                         w = ow * (h/oh);
28246                         y += th - h;
28247                         x += tw - w;
28248                        break;
28249
28250                 }
28251             }
28252             if (pos == 'hdrag') {
28253                 w = ow;
28254             }
28255             this.proxy.setBounds(x, y, w, h);
28256             if(this.dynamic){
28257                 this.resizeElement();
28258             }
28259             }catch(e){}
28260         }
28261     },
28262
28263     // private
28264     handleOver : function(){
28265         if(this.enabled){
28266             this.el.addClass("x-resizable-over");
28267         }
28268     },
28269
28270     // private
28271     handleOut : function(){
28272         if(!this.resizing){
28273             this.el.removeClass("x-resizable-over");
28274         }
28275     },
28276
28277     /**
28278      * Returns the element this component is bound to.
28279      * @return {Roo.Element}
28280      */
28281     getEl : function(){
28282         return this.el;
28283     },
28284
28285     /**
28286      * Returns the resizeChild element (or null).
28287      * @return {Roo.Element}
28288      */
28289     getResizeChild : function(){
28290         return this.resizeChild;
28291     },
28292
28293     /**
28294      * Destroys this resizable. If the element was wrapped and
28295      * removeEl is not true then the element remains.
28296      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28297      */
28298     destroy : function(removeEl){
28299         this.proxy.remove();
28300         if(this.overlay){
28301             this.overlay.removeAllListeners();
28302             this.overlay.remove();
28303         }
28304         var ps = Roo.Resizable.positions;
28305         for(var k in ps){
28306             if(typeof ps[k] != "function" && this[ps[k]]){
28307                 var h = this[ps[k]];
28308                 h.el.removeAllListeners();
28309                 h.el.remove();
28310             }
28311         }
28312         if(removeEl){
28313             this.el.update("");
28314             this.el.remove();
28315         }
28316     }
28317 });
28318
28319 // private
28320 // hash to map config positions to true positions
28321 Roo.Resizable.positions = {
28322     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28323     hd: "hdrag"
28324 };
28325
28326 // private
28327 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28328     if(!this.tpl){
28329         // only initialize the template if resizable is used
28330         var tpl = Roo.DomHelper.createTemplate(
28331             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28332         );
28333         tpl.compile();
28334         Roo.Resizable.Handle.prototype.tpl = tpl;
28335     }
28336     this.position = pos;
28337     this.rz = rz;
28338     // show north drag fro topdra
28339     var handlepos = pos == 'hdrag' ? 'north' : pos;
28340     
28341     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28342     if (pos == 'hdrag') {
28343         this.el.setStyle('cursor', 'pointer');
28344     }
28345     this.el.unselectable();
28346     if(transparent){
28347         this.el.setOpacity(0);
28348     }
28349     this.el.on("mousedown", this.onMouseDown, this);
28350     if(!disableTrackOver){
28351         this.el.on("mouseover", this.onMouseOver, this);
28352         this.el.on("mouseout", this.onMouseOut, this);
28353     }
28354 };
28355
28356 // private
28357 Roo.Resizable.Handle.prototype = {
28358     afterResize : function(rz){
28359         // do nothing
28360     },
28361     // private
28362     onMouseDown : function(e){
28363         this.rz.onMouseDown(this, e);
28364     },
28365     // private
28366     onMouseOver : function(e){
28367         this.rz.handleOver(this, e);
28368     },
28369     // private
28370     onMouseOut : function(e){
28371         this.rz.handleOut(this, e);
28372     }
28373 };/*
28374  * Based on:
28375  * Ext JS Library 1.1.1
28376  * Copyright(c) 2006-2007, Ext JS, LLC.
28377  *
28378  * Originally Released Under LGPL - original licence link has changed is not relivant.
28379  *
28380  * Fork - LGPL
28381  * <script type="text/javascript">
28382  */
28383
28384 /**
28385  * @class Roo.Editor
28386  * @extends Roo.Component
28387  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28388  * @constructor
28389  * Create a new Editor
28390  * @param {Roo.form.Field} field The Field object (or descendant)
28391  * @param {Object} config The config object
28392  */
28393 Roo.Editor = function(field, config){
28394     Roo.Editor.superclass.constructor.call(this, config);
28395     this.field = field;
28396     this.addEvents({
28397         /**
28398              * @event beforestartedit
28399              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28400              * false from the handler of this event.
28401              * @param {Editor} this
28402              * @param {Roo.Element} boundEl The underlying element bound to this editor
28403              * @param {Mixed} value The field value being set
28404              */
28405         "beforestartedit" : true,
28406         /**
28407              * @event startedit
28408              * Fires when this editor is displayed
28409              * @param {Roo.Element} boundEl The underlying element bound to this editor
28410              * @param {Mixed} value The starting field value
28411              */
28412         "startedit" : true,
28413         /**
28414              * @event beforecomplete
28415              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28416              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28417              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28418              * event will not fire since no edit actually occurred.
28419              * @param {Editor} this
28420              * @param {Mixed} value The current field value
28421              * @param {Mixed} startValue The original field value
28422              */
28423         "beforecomplete" : true,
28424         /**
28425              * @event complete
28426              * Fires after editing is complete and any changed value has been written to the underlying field.
28427              * @param {Editor} this
28428              * @param {Mixed} value The current field value
28429              * @param {Mixed} startValue The original field value
28430              */
28431         "complete" : true,
28432         /**
28433          * @event specialkey
28434          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28435          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28436          * @param {Roo.form.Field} this
28437          * @param {Roo.EventObject} e The event object
28438          */
28439         "specialkey" : true
28440     });
28441 };
28442
28443 Roo.extend(Roo.Editor, Roo.Component, {
28444     /**
28445      * @cfg {Boolean/String} autosize
28446      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28447      * or "height" to adopt the height only (defaults to false)
28448      */
28449     /**
28450      * @cfg {Boolean} revertInvalid
28451      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28452      * validation fails (defaults to true)
28453      */
28454     /**
28455      * @cfg {Boolean} ignoreNoChange
28456      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28457      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28458      * will never be ignored.
28459      */
28460     /**
28461      * @cfg {Boolean} hideEl
28462      * False to keep the bound element visible while the editor is displayed (defaults to true)
28463      */
28464     /**
28465      * @cfg {Mixed} value
28466      * The data value of the underlying field (defaults to "")
28467      */
28468     value : "",
28469     /**
28470      * @cfg {String} alignment
28471      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28472      */
28473     alignment: "c-c?",
28474     /**
28475      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28476      * for bottom-right shadow (defaults to "frame")
28477      */
28478     shadow : "frame",
28479     /**
28480      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28481      */
28482     constrain : false,
28483     /**
28484      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28485      */
28486     completeOnEnter : false,
28487     /**
28488      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28489      */
28490     cancelOnEsc : false,
28491     /**
28492      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28493      */
28494     updateEl : false,
28495
28496     // private
28497     onRender : function(ct, position){
28498         this.el = new Roo.Layer({
28499             shadow: this.shadow,
28500             cls: "x-editor",
28501             parentEl : ct,
28502             shim : this.shim,
28503             shadowOffset:4,
28504             id: this.id,
28505             constrain: this.constrain
28506         });
28507         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28508         if(this.field.msgTarget != 'title'){
28509             this.field.msgTarget = 'qtip';
28510         }
28511         this.field.render(this.el);
28512         if(Roo.isGecko){
28513             this.field.el.dom.setAttribute('autocomplete', 'off');
28514         }
28515         this.field.on("specialkey", this.onSpecialKey, this);
28516         if(this.swallowKeys){
28517             this.field.el.swallowEvent(['keydown','keypress']);
28518         }
28519         this.field.show();
28520         this.field.on("blur", this.onBlur, this);
28521         if(this.field.grow){
28522             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28523         }
28524     },
28525
28526     onSpecialKey : function(field, e)
28527     {
28528         //Roo.log('editor onSpecialKey');
28529         if(this.completeOnEnter && e.getKey() == e.ENTER){
28530             e.stopEvent();
28531             this.completeEdit();
28532             return;
28533         }
28534         // do not fire special key otherwise it might hide close the editor...
28535         if(e.getKey() == e.ENTER){    
28536             return;
28537         }
28538         if(this.cancelOnEsc && e.getKey() == e.ESC){
28539             this.cancelEdit();
28540             return;
28541         } 
28542         this.fireEvent('specialkey', field, e);
28543     
28544     },
28545
28546     /**
28547      * Starts the editing process and shows the editor.
28548      * @param {String/HTMLElement/Element} el The element to edit
28549      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28550       * to the innerHTML of el.
28551      */
28552     startEdit : function(el, value){
28553         if(this.editing){
28554             this.completeEdit();
28555         }
28556         this.boundEl = Roo.get(el);
28557         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28558         if(!this.rendered){
28559             this.render(this.parentEl || document.body);
28560         }
28561         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28562             return;
28563         }
28564         this.startValue = v;
28565         this.field.setValue(v);
28566         if(this.autoSize){
28567             var sz = this.boundEl.getSize();
28568             switch(this.autoSize){
28569                 case "width":
28570                 this.setSize(sz.width,  "");
28571                 break;
28572                 case "height":
28573                 this.setSize("",  sz.height);
28574                 break;
28575                 default:
28576                 this.setSize(sz.width,  sz.height);
28577             }
28578         }
28579         this.el.alignTo(this.boundEl, this.alignment);
28580         this.editing = true;
28581         if(Roo.QuickTips){
28582             Roo.QuickTips.disable();
28583         }
28584         this.show();
28585     },
28586
28587     /**
28588      * Sets the height and width of this editor.
28589      * @param {Number} width The new width
28590      * @param {Number} height The new height
28591      */
28592     setSize : function(w, h){
28593         this.field.setSize(w, h);
28594         if(this.el){
28595             this.el.sync();
28596         }
28597     },
28598
28599     /**
28600      * Realigns the editor to the bound field based on the current alignment config value.
28601      */
28602     realign : function(){
28603         this.el.alignTo(this.boundEl, this.alignment);
28604     },
28605
28606     /**
28607      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28608      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28609      */
28610     completeEdit : function(remainVisible){
28611         if(!this.editing){
28612             return;
28613         }
28614         var v = this.getValue();
28615         if(this.revertInvalid !== false && !this.field.isValid()){
28616             v = this.startValue;
28617             this.cancelEdit(true);
28618         }
28619         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28620             this.editing = false;
28621             this.hide();
28622             return;
28623         }
28624         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28625             this.editing = false;
28626             if(this.updateEl && this.boundEl){
28627                 this.boundEl.update(v);
28628             }
28629             if(remainVisible !== true){
28630                 this.hide();
28631             }
28632             this.fireEvent("complete", this, v, this.startValue);
28633         }
28634     },
28635
28636     // private
28637     onShow : function(){
28638         this.el.show();
28639         if(this.hideEl !== false){
28640             this.boundEl.hide();
28641         }
28642         this.field.show();
28643         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28644             this.fixIEFocus = true;
28645             this.deferredFocus.defer(50, this);
28646         }else{
28647             this.field.focus();
28648         }
28649         this.fireEvent("startedit", this.boundEl, this.startValue);
28650     },
28651
28652     deferredFocus : function(){
28653         if(this.editing){
28654             this.field.focus();
28655         }
28656     },
28657
28658     /**
28659      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28660      * reverted to the original starting value.
28661      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28662      * cancel (defaults to false)
28663      */
28664     cancelEdit : function(remainVisible){
28665         if(this.editing){
28666             this.setValue(this.startValue);
28667             if(remainVisible !== true){
28668                 this.hide();
28669             }
28670         }
28671     },
28672
28673     // private
28674     onBlur : function(){
28675         if(this.allowBlur !== true && this.editing){
28676             this.completeEdit();
28677         }
28678     },
28679
28680     // private
28681     onHide : function(){
28682         if(this.editing){
28683             this.completeEdit();
28684             return;
28685         }
28686         this.field.blur();
28687         if(this.field.collapse){
28688             this.field.collapse();
28689         }
28690         this.el.hide();
28691         if(this.hideEl !== false){
28692             this.boundEl.show();
28693         }
28694         if(Roo.QuickTips){
28695             Roo.QuickTips.enable();
28696         }
28697     },
28698
28699     /**
28700      * Sets the data value of the editor
28701      * @param {Mixed} value Any valid value supported by the underlying field
28702      */
28703     setValue : function(v){
28704         this.field.setValue(v);
28705     },
28706
28707     /**
28708      * Gets the data value of the editor
28709      * @return {Mixed} The data value
28710      */
28711     getValue : function(){
28712         return this.field.getValue();
28713     }
28714 });/*
28715  * Based on:
28716  * Ext JS Library 1.1.1
28717  * Copyright(c) 2006-2007, Ext JS, LLC.
28718  *
28719  * Originally Released Under LGPL - original licence link has changed is not relivant.
28720  *
28721  * Fork - LGPL
28722  * <script type="text/javascript">
28723  */
28724  
28725 /**
28726  * @class Roo.BasicDialog
28727  * @extends Roo.util.Observable
28728  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28729  * <pre><code>
28730 var dlg = new Roo.BasicDialog("my-dlg", {
28731     height: 200,
28732     width: 300,
28733     minHeight: 100,
28734     minWidth: 150,
28735     modal: true,
28736     proxyDrag: true,
28737     shadow: true
28738 });
28739 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28740 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28741 dlg.addButton('Cancel', dlg.hide, dlg);
28742 dlg.show();
28743 </code></pre>
28744   <b>A Dialog should always be a direct child of the body element.</b>
28745  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28746  * @cfg {String} title Default text to display in the title bar (defaults to null)
28747  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28748  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28749  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28750  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28751  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28752  * (defaults to null with no animation)
28753  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28754  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28755  * property for valid values (defaults to 'all')
28756  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28757  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28758  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28759  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28760  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28761  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28762  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28763  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28764  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28765  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28766  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28767  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28768  * draggable = true (defaults to false)
28769  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28770  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28771  * shadow (defaults to false)
28772  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28773  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28774  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28775  * @cfg {Array} buttons Array of buttons
28776  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28777  * @constructor
28778  * Create a new BasicDialog.
28779  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28780  * @param {Object} config Configuration options
28781  */
28782 Roo.BasicDialog = function(el, config){
28783     this.el = Roo.get(el);
28784     var dh = Roo.DomHelper;
28785     if(!this.el && config && config.autoCreate){
28786         if(typeof config.autoCreate == "object"){
28787             if(!config.autoCreate.id){
28788                 config.autoCreate.id = el;
28789             }
28790             this.el = dh.append(document.body,
28791                         config.autoCreate, true);
28792         }else{
28793             this.el = dh.append(document.body,
28794                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28795         }
28796     }
28797     el = this.el;
28798     el.setDisplayed(true);
28799     el.hide = this.hideAction;
28800     this.id = el.id;
28801     el.addClass("x-dlg");
28802
28803     Roo.apply(this, config);
28804
28805     this.proxy = el.createProxy("x-dlg-proxy");
28806     this.proxy.hide = this.hideAction;
28807     this.proxy.setOpacity(.5);
28808     this.proxy.hide();
28809
28810     if(config.width){
28811         el.setWidth(config.width);
28812     }
28813     if(config.height){
28814         el.setHeight(config.height);
28815     }
28816     this.size = el.getSize();
28817     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28818         this.xy = [config.x,config.y];
28819     }else{
28820         this.xy = el.getCenterXY(true);
28821     }
28822     /** The header element @type Roo.Element */
28823     this.header = el.child("> .x-dlg-hd");
28824     /** The body element @type Roo.Element */
28825     this.body = el.child("> .x-dlg-bd");
28826     /** The footer element @type Roo.Element */
28827     this.footer = el.child("> .x-dlg-ft");
28828
28829     if(!this.header){
28830         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28831     }
28832     if(!this.body){
28833         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28834     }
28835
28836     this.header.unselectable();
28837     if(this.title){
28838         this.header.update(this.title);
28839     }
28840     // this element allows the dialog to be focused for keyboard event
28841     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28842     this.focusEl.swallowEvent("click", true);
28843
28844     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28845
28846     // wrap the body and footer for special rendering
28847     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28848     if(this.footer){
28849         this.bwrap.dom.appendChild(this.footer.dom);
28850     }
28851
28852     this.bg = this.el.createChild({
28853         tag: "div", cls:"x-dlg-bg",
28854         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28855     });
28856     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28857
28858
28859     if(this.autoScroll !== false && !this.autoTabs){
28860         this.body.setStyle("overflow", "auto");
28861     }
28862
28863     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28864
28865     if(this.closable !== false){
28866         this.el.addClass("x-dlg-closable");
28867         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28868         this.close.on("click", this.closeClick, this);
28869         this.close.addClassOnOver("x-dlg-close-over");
28870     }
28871     if(this.collapsible !== false){
28872         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28873         this.collapseBtn.on("click", this.collapseClick, this);
28874         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28875         this.header.on("dblclick", this.collapseClick, this);
28876     }
28877     if(this.resizable !== false){
28878         this.el.addClass("x-dlg-resizable");
28879         this.resizer = new Roo.Resizable(el, {
28880             minWidth: this.minWidth || 80,
28881             minHeight:this.minHeight || 80,
28882             handles: this.resizeHandles || "all",
28883             pinned: true
28884         });
28885         this.resizer.on("beforeresize", this.beforeResize, this);
28886         this.resizer.on("resize", this.onResize, this);
28887     }
28888     if(this.draggable !== false){
28889         el.addClass("x-dlg-draggable");
28890         if (!this.proxyDrag) {
28891             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28892         }
28893         else {
28894             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28895         }
28896         dd.setHandleElId(this.header.id);
28897         dd.endDrag = this.endMove.createDelegate(this);
28898         dd.startDrag = this.startMove.createDelegate(this);
28899         dd.onDrag = this.onDrag.createDelegate(this);
28900         dd.scroll = false;
28901         this.dd = dd;
28902     }
28903     if(this.modal){
28904         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28905         this.mask.enableDisplayMode("block");
28906         this.mask.hide();
28907         this.el.addClass("x-dlg-modal");
28908     }
28909     if(this.shadow){
28910         this.shadow = new Roo.Shadow({
28911             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28912             offset : this.shadowOffset
28913         });
28914     }else{
28915         this.shadowOffset = 0;
28916     }
28917     if(Roo.useShims && this.shim !== false){
28918         this.shim = this.el.createShim();
28919         this.shim.hide = this.hideAction;
28920         this.shim.hide();
28921     }else{
28922         this.shim = false;
28923     }
28924     if(this.autoTabs){
28925         this.initTabs();
28926     }
28927     if (this.buttons) { 
28928         var bts= this.buttons;
28929         this.buttons = [];
28930         Roo.each(bts, function(b) {
28931             this.addButton(b);
28932         }, this);
28933     }
28934     
28935     
28936     this.addEvents({
28937         /**
28938          * @event keydown
28939          * Fires when a key is pressed
28940          * @param {Roo.BasicDialog} this
28941          * @param {Roo.EventObject} e
28942          */
28943         "keydown" : true,
28944         /**
28945          * @event move
28946          * Fires when this dialog is moved by the user.
28947          * @param {Roo.BasicDialog} this
28948          * @param {Number} x The new page X
28949          * @param {Number} y The new page Y
28950          */
28951         "move" : true,
28952         /**
28953          * @event resize
28954          * Fires when this dialog is resized by the user.
28955          * @param {Roo.BasicDialog} this
28956          * @param {Number} width The new width
28957          * @param {Number} height The new height
28958          */
28959         "resize" : true,
28960         /**
28961          * @event beforehide
28962          * Fires before this dialog is hidden.
28963          * @param {Roo.BasicDialog} this
28964          */
28965         "beforehide" : true,
28966         /**
28967          * @event hide
28968          * Fires when this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "hide" : true,
28972         /**
28973          * @event beforeshow
28974          * Fires before this dialog is shown.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "beforeshow" : true,
28978         /**
28979          * @event show
28980          * Fires when this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "show" : true
28984     });
28985     el.on("keydown", this.onKeyDown, this);
28986     el.on("mousedown", this.toFront, this);
28987     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28988     this.el.hide();
28989     Roo.DialogManager.register(this);
28990     Roo.BasicDialog.superclass.constructor.call(this);
28991 };
28992
28993 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28994     shadowOffset: Roo.isIE ? 6 : 5,
28995     minHeight: 80,
28996     minWidth: 200,
28997     minButtonWidth: 75,
28998     defaultButton: null,
28999     buttonAlign: "right",
29000     tabTag: 'div',
29001     firstShow: true,
29002
29003     /**
29004      * Sets the dialog title text
29005      * @param {String} text The title text to display
29006      * @return {Roo.BasicDialog} this
29007      */
29008     setTitle : function(text){
29009         this.header.update(text);
29010         return this;
29011     },
29012
29013     // private
29014     closeClick : function(){
29015         this.hide();
29016     },
29017
29018     // private
29019     collapseClick : function(){
29020         this[this.collapsed ? "expand" : "collapse"]();
29021     },
29022
29023     /**
29024      * Collapses the dialog to its minimized state (only the title bar is visible).
29025      * Equivalent to the user clicking the collapse dialog button.
29026      */
29027     collapse : function(){
29028         if(!this.collapsed){
29029             this.collapsed = true;
29030             this.el.addClass("x-dlg-collapsed");
29031             this.restoreHeight = this.el.getHeight();
29032             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29033         }
29034     },
29035
29036     /**
29037      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29038      * clicking the expand dialog button.
29039      */
29040     expand : function(){
29041         if(this.collapsed){
29042             this.collapsed = false;
29043             this.el.removeClass("x-dlg-collapsed");
29044             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29045         }
29046     },
29047
29048     /**
29049      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29050      * @return {Roo.TabPanel} The tabs component
29051      */
29052     initTabs : function(){
29053         var tabs = this.getTabs();
29054         while(tabs.getTab(0)){
29055             tabs.removeTab(0);
29056         }
29057         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29058             var dom = el.dom;
29059             tabs.addTab(Roo.id(dom), dom.title);
29060             dom.title = "";
29061         });
29062         tabs.activate(0);
29063         return tabs;
29064     },
29065
29066     // private
29067     beforeResize : function(){
29068         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29069     },
29070
29071     // private
29072     onResize : function(){
29073         this.refreshSize();
29074         this.syncBodyHeight();
29075         this.adjustAssets();
29076         this.focus();
29077         this.fireEvent("resize", this, this.size.width, this.size.height);
29078     },
29079
29080     // private
29081     onKeyDown : function(e){
29082         if(this.isVisible()){
29083             this.fireEvent("keydown", this, e);
29084         }
29085     },
29086
29087     /**
29088      * Resizes the dialog.
29089      * @param {Number} width
29090      * @param {Number} height
29091      * @return {Roo.BasicDialog} this
29092      */
29093     resizeTo : function(width, height){
29094         this.el.setSize(width, height);
29095         this.size = {width: width, height: height};
29096         this.syncBodyHeight();
29097         if(this.fixedcenter){
29098             this.center();
29099         }
29100         if(this.isVisible()){
29101             this.constrainXY();
29102             this.adjustAssets();
29103         }
29104         this.fireEvent("resize", this, width, height);
29105         return this;
29106     },
29107
29108
29109     /**
29110      * Resizes the dialog to fit the specified content size.
29111      * @param {Number} width
29112      * @param {Number} height
29113      * @return {Roo.BasicDialog} this
29114      */
29115     setContentSize : function(w, h){
29116         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29117         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29118         //if(!this.el.isBorderBox()){
29119             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29120             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29121         //}
29122         if(this.tabs){
29123             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29124             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29125         }
29126         this.resizeTo(w, h);
29127         return this;
29128     },
29129
29130     /**
29131      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29132      * executed in response to a particular key being pressed while the dialog is active.
29133      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29134      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29135      * @param {Function} fn The function to call
29136      * @param {Object} scope (optional) The scope of the function
29137      * @return {Roo.BasicDialog} this
29138      */
29139     addKeyListener : function(key, fn, scope){
29140         var keyCode, shift, ctrl, alt;
29141         if(typeof key == "object" && !(key instanceof Array)){
29142             keyCode = key["key"];
29143             shift = key["shift"];
29144             ctrl = key["ctrl"];
29145             alt = key["alt"];
29146         }else{
29147             keyCode = key;
29148         }
29149         var handler = function(dlg, e){
29150             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29151                 var k = e.getKey();
29152                 if(keyCode instanceof Array){
29153                     for(var i = 0, len = keyCode.length; i < len; i++){
29154                         if(keyCode[i] == k){
29155                           fn.call(scope || window, dlg, k, e);
29156                           return;
29157                         }
29158                     }
29159                 }else{
29160                     if(k == keyCode){
29161                         fn.call(scope || window, dlg, k, e);
29162                     }
29163                 }
29164             }
29165         };
29166         this.on("keydown", handler);
29167         return this;
29168     },
29169
29170     /**
29171      * Returns the TabPanel component (creates it if it doesn't exist).
29172      * Note: If you wish to simply check for the existence of tabs without creating them,
29173      * check for a null 'tabs' property.
29174      * @return {Roo.TabPanel} The tabs component
29175      */
29176     getTabs : function(){
29177         if(!this.tabs){
29178             this.el.addClass("x-dlg-auto-tabs");
29179             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29180             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29181         }
29182         return this.tabs;
29183     },
29184
29185     /**
29186      * Adds a button to the footer section of the dialog.
29187      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29188      * object or a valid Roo.DomHelper element config
29189      * @param {Function} handler The function called when the button is clicked
29190      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29191      * @return {Roo.Button} The new button
29192      */
29193     addButton : function(config, handler, scope){
29194         var dh = Roo.DomHelper;
29195         if(!this.footer){
29196             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29197         }
29198         if(!this.btnContainer){
29199             var tb = this.footer.createChild({
29200
29201                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29202                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29203             }, null, true);
29204             this.btnContainer = tb.firstChild.firstChild.firstChild;
29205         }
29206         var bconfig = {
29207             handler: handler,
29208             scope: scope,
29209             minWidth: this.minButtonWidth,
29210             hideParent:true
29211         };
29212         if(typeof config == "string"){
29213             bconfig.text = config;
29214         }else{
29215             if(config.tag){
29216                 bconfig.dhconfig = config;
29217             }else{
29218                 Roo.apply(bconfig, config);
29219             }
29220         }
29221         var fc = false;
29222         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29223             bconfig.position = Math.max(0, bconfig.position);
29224             fc = this.btnContainer.childNodes[bconfig.position];
29225         }
29226          
29227         var btn = new Roo.Button(
29228             fc ? 
29229                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29230                 : this.btnContainer.appendChild(document.createElement("td")),
29231             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29232             bconfig
29233         );
29234         this.syncBodyHeight();
29235         if(!this.buttons){
29236             /**
29237              * Array of all the buttons that have been added to this dialog via addButton
29238              * @type Array
29239              */
29240             this.buttons = [];
29241         }
29242         this.buttons.push(btn);
29243         return btn;
29244     },
29245
29246     /**
29247      * Sets the default button to be focused when the dialog is displayed.
29248      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29249      * @return {Roo.BasicDialog} this
29250      */
29251     setDefaultButton : function(btn){
29252         this.defaultButton = btn;
29253         return this;
29254     },
29255
29256     // private
29257     getHeaderFooterHeight : function(safe){
29258         var height = 0;
29259         if(this.header){
29260            height += this.header.getHeight();
29261         }
29262         if(this.footer){
29263            var fm = this.footer.getMargins();
29264             height += (this.footer.getHeight()+fm.top+fm.bottom);
29265         }
29266         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29267         height += this.centerBg.getPadding("tb");
29268         return height;
29269     },
29270
29271     // private
29272     syncBodyHeight : function(){
29273         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29274         var height = this.size.height - this.getHeaderFooterHeight(false);
29275         bd.setHeight(height-bd.getMargins("tb"));
29276         var hh = this.header.getHeight();
29277         var h = this.size.height-hh;
29278         cb.setHeight(h);
29279         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29280         bw.setHeight(h-cb.getPadding("tb"));
29281         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29282         bd.setWidth(bw.getWidth(true));
29283         if(this.tabs){
29284             this.tabs.syncHeight();
29285             if(Roo.isIE){
29286                 this.tabs.el.repaint();
29287             }
29288         }
29289     },
29290
29291     /**
29292      * Restores the previous state of the dialog if Roo.state is configured.
29293      * @return {Roo.BasicDialog} this
29294      */
29295     restoreState : function(){
29296         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29297         if(box && box.width){
29298             this.xy = [box.x, box.y];
29299             this.resizeTo(box.width, box.height);
29300         }
29301         return this;
29302     },
29303
29304     // private
29305     beforeShow : function(){
29306         this.expand();
29307         if(this.fixedcenter){
29308             this.xy = this.el.getCenterXY(true);
29309         }
29310         if(this.modal){
29311             Roo.get(document.body).addClass("x-body-masked");
29312             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29313             this.mask.show();
29314         }
29315         this.constrainXY();
29316     },
29317
29318     // private
29319     animShow : function(){
29320         var b = Roo.get(this.animateTarget).getBox();
29321         this.proxy.setSize(b.width, b.height);
29322         this.proxy.setLocation(b.x, b.y);
29323         this.proxy.show();
29324         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29325                     true, .35, this.showEl.createDelegate(this));
29326     },
29327
29328     /**
29329      * Shows the dialog.
29330      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29331      * @return {Roo.BasicDialog} this
29332      */
29333     show : function(animateTarget){
29334         if (this.fireEvent("beforeshow", this) === false){
29335             return;
29336         }
29337         if(this.syncHeightBeforeShow){
29338             this.syncBodyHeight();
29339         }else if(this.firstShow){
29340             this.firstShow = false;
29341             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29342         }
29343         this.animateTarget = animateTarget || this.animateTarget;
29344         if(!this.el.isVisible()){
29345             this.beforeShow();
29346             if(this.animateTarget && Roo.get(this.animateTarget)){
29347                 this.animShow();
29348             }else{
29349                 this.showEl();
29350             }
29351         }
29352         return this;
29353     },
29354
29355     // private
29356     showEl : function(){
29357         this.proxy.hide();
29358         this.el.setXY(this.xy);
29359         this.el.show();
29360         this.adjustAssets(true);
29361         this.toFront();
29362         this.focus();
29363         // IE peekaboo bug - fix found by Dave Fenwick
29364         if(Roo.isIE){
29365             this.el.repaint();
29366         }
29367         this.fireEvent("show", this);
29368     },
29369
29370     /**
29371      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29372      * dialog itself will receive focus.
29373      */
29374     focus : function(){
29375         if(this.defaultButton){
29376             this.defaultButton.focus();
29377         }else{
29378             this.focusEl.focus();
29379         }
29380     },
29381
29382     // private
29383     constrainXY : function(){
29384         if(this.constraintoviewport !== false){
29385             if(!this.viewSize){
29386                 if(this.container){
29387                     var s = this.container.getSize();
29388                     this.viewSize = [s.width, s.height];
29389                 }else{
29390                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29391                 }
29392             }
29393             var s = Roo.get(this.container||document).getScroll();
29394
29395             var x = this.xy[0], y = this.xy[1];
29396             var w = this.size.width, h = this.size.height;
29397             var vw = this.viewSize[0], vh = this.viewSize[1];
29398             // only move it if it needs it
29399             var moved = false;
29400             // first validate right/bottom
29401             if(x + w > vw+s.left){
29402                 x = vw - w;
29403                 moved = true;
29404             }
29405             if(y + h > vh+s.top){
29406                 y = vh - h;
29407                 moved = true;
29408             }
29409             // then make sure top/left isn't negative
29410             if(x < s.left){
29411                 x = s.left;
29412                 moved = true;
29413             }
29414             if(y < s.top){
29415                 y = s.top;
29416                 moved = true;
29417             }
29418             if(moved){
29419                 // cache xy
29420                 this.xy = [x, y];
29421                 if(this.isVisible()){
29422                     this.el.setLocation(x, y);
29423                     this.adjustAssets();
29424                 }
29425             }
29426         }
29427     },
29428
29429     // private
29430     onDrag : function(){
29431         if(!this.proxyDrag){
29432             this.xy = this.el.getXY();
29433             this.adjustAssets();
29434         }
29435     },
29436
29437     // private
29438     adjustAssets : function(doShow){
29439         var x = this.xy[0], y = this.xy[1];
29440         var w = this.size.width, h = this.size.height;
29441         if(doShow === true){
29442             if(this.shadow){
29443                 this.shadow.show(this.el);
29444             }
29445             if(this.shim){
29446                 this.shim.show();
29447             }
29448         }
29449         if(this.shadow && this.shadow.isVisible()){
29450             this.shadow.show(this.el);
29451         }
29452         if(this.shim && this.shim.isVisible()){
29453             this.shim.setBounds(x, y, w, h);
29454         }
29455     },
29456
29457     // private
29458     adjustViewport : function(w, h){
29459         if(!w || !h){
29460             w = Roo.lib.Dom.getViewWidth();
29461             h = Roo.lib.Dom.getViewHeight();
29462         }
29463         // cache the size
29464         this.viewSize = [w, h];
29465         if(this.modal && this.mask.isVisible()){
29466             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29467             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29468         }
29469         if(this.isVisible()){
29470             this.constrainXY();
29471         }
29472     },
29473
29474     /**
29475      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29476      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29477      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29478      */
29479     destroy : function(removeEl){
29480         if(this.isVisible()){
29481             this.animateTarget = null;
29482             this.hide();
29483         }
29484         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29485         if(this.tabs){
29486             this.tabs.destroy(removeEl);
29487         }
29488         Roo.destroy(
29489              this.shim,
29490              this.proxy,
29491              this.resizer,
29492              this.close,
29493              this.mask
29494         );
29495         if(this.dd){
29496             this.dd.unreg();
29497         }
29498         if(this.buttons){
29499            for(var i = 0, len = this.buttons.length; i < len; i++){
29500                this.buttons[i].destroy();
29501            }
29502         }
29503         this.el.removeAllListeners();
29504         if(removeEl === true){
29505             this.el.update("");
29506             this.el.remove();
29507         }
29508         Roo.DialogManager.unregister(this);
29509     },
29510
29511     // private
29512     startMove : function(){
29513         if(this.proxyDrag){
29514             this.proxy.show();
29515         }
29516         if(this.constraintoviewport !== false){
29517             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29518         }
29519     },
29520
29521     // private
29522     endMove : function(){
29523         if(!this.proxyDrag){
29524             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29525         }else{
29526             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29527             this.proxy.hide();
29528         }
29529         this.refreshSize();
29530         this.adjustAssets();
29531         this.focus();
29532         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29533     },
29534
29535     /**
29536      * Brings this dialog to the front of any other visible dialogs
29537      * @return {Roo.BasicDialog} this
29538      */
29539     toFront : function(){
29540         Roo.DialogManager.bringToFront(this);
29541         return this;
29542     },
29543
29544     /**
29545      * Sends this dialog to the back (under) of any other visible dialogs
29546      * @return {Roo.BasicDialog} this
29547      */
29548     toBack : function(){
29549         Roo.DialogManager.sendToBack(this);
29550         return this;
29551     },
29552
29553     /**
29554      * Centers this dialog in the viewport
29555      * @return {Roo.BasicDialog} this
29556      */
29557     center : function(){
29558         var xy = this.el.getCenterXY(true);
29559         this.moveTo(xy[0], xy[1]);
29560         return this;
29561     },
29562
29563     /**
29564      * Moves the dialog's top-left corner to the specified point
29565      * @param {Number} x
29566      * @param {Number} y
29567      * @return {Roo.BasicDialog} this
29568      */
29569     moveTo : function(x, y){
29570         this.xy = [x,y];
29571         if(this.isVisible()){
29572             this.el.setXY(this.xy);
29573             this.adjustAssets();
29574         }
29575         return this;
29576     },
29577
29578     /**
29579      * Aligns the dialog to the specified element
29580      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29581      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29582      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29583      * @return {Roo.BasicDialog} this
29584      */
29585     alignTo : function(element, position, offsets){
29586         this.xy = this.el.getAlignToXY(element, position, offsets);
29587         if(this.isVisible()){
29588             this.el.setXY(this.xy);
29589             this.adjustAssets();
29590         }
29591         return this;
29592     },
29593
29594     /**
29595      * Anchors an element to another element and realigns it when the window is resized.
29596      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29597      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29598      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29599      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29600      * is a number, it is used as the buffer delay (defaults to 50ms).
29601      * @return {Roo.BasicDialog} this
29602      */
29603     anchorTo : function(el, alignment, offsets, monitorScroll){
29604         var action = function(){
29605             this.alignTo(el, alignment, offsets);
29606         };
29607         Roo.EventManager.onWindowResize(action, this);
29608         var tm = typeof monitorScroll;
29609         if(tm != 'undefined'){
29610             Roo.EventManager.on(window, 'scroll', action, this,
29611                 {buffer: tm == 'number' ? monitorScroll : 50});
29612         }
29613         action.call(this);
29614         return this;
29615     },
29616
29617     /**
29618      * Returns true if the dialog is visible
29619      * @return {Boolean}
29620      */
29621     isVisible : function(){
29622         return this.el.isVisible();
29623     },
29624
29625     // private
29626     animHide : function(callback){
29627         var b = Roo.get(this.animateTarget).getBox();
29628         this.proxy.show();
29629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29630         this.el.hide();
29631         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29632                     this.hideEl.createDelegate(this, [callback]));
29633     },
29634
29635     /**
29636      * Hides the dialog.
29637      * @param {Function} callback (optional) Function to call when the dialog is hidden
29638      * @return {Roo.BasicDialog} this
29639      */
29640     hide : function(callback){
29641         if (this.fireEvent("beforehide", this) === false){
29642             return;
29643         }
29644         if(this.shadow){
29645             this.shadow.hide();
29646         }
29647         if(this.shim) {
29648           this.shim.hide();
29649         }
29650         // sometimes animateTarget seems to get set.. causing problems...
29651         // this just double checks..
29652         if(this.animateTarget && Roo.get(this.animateTarget)) {
29653            this.animHide(callback);
29654         }else{
29655             this.el.hide();
29656             this.hideEl(callback);
29657         }
29658         return this;
29659     },
29660
29661     // private
29662     hideEl : function(callback){
29663         this.proxy.hide();
29664         if(this.modal){
29665             this.mask.hide();
29666             Roo.get(document.body).removeClass("x-body-masked");
29667         }
29668         this.fireEvent("hide", this);
29669         if(typeof callback == "function"){
29670             callback();
29671         }
29672     },
29673
29674     // private
29675     hideAction : function(){
29676         this.setLeft("-10000px");
29677         this.setTop("-10000px");
29678         this.setStyle("visibility", "hidden");
29679     },
29680
29681     // private
29682     refreshSize : function(){
29683         this.size = this.el.getSize();
29684         this.xy = this.el.getXY();
29685         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29686     },
29687
29688     // private
29689     // z-index is managed by the DialogManager and may be overwritten at any time
29690     setZIndex : function(index){
29691         if(this.modal){
29692             this.mask.setStyle("z-index", index);
29693         }
29694         if(this.shim){
29695             this.shim.setStyle("z-index", ++index);
29696         }
29697         if(this.shadow){
29698             this.shadow.setZIndex(++index);
29699         }
29700         this.el.setStyle("z-index", ++index);
29701         if(this.proxy){
29702             this.proxy.setStyle("z-index", ++index);
29703         }
29704         if(this.resizer){
29705             this.resizer.proxy.setStyle("z-index", ++index);
29706         }
29707
29708         this.lastZIndex = index;
29709     },
29710
29711     /**
29712      * Returns the element for this dialog
29713      * @return {Roo.Element} The underlying dialog Element
29714      */
29715     getEl : function(){
29716         return this.el;
29717     }
29718 });
29719
29720 /**
29721  * @class Roo.DialogManager
29722  * Provides global access to BasicDialogs that have been created and
29723  * support for z-indexing (layering) multiple open dialogs.
29724  */
29725 Roo.DialogManager = function(){
29726     var list = {};
29727     var accessList = [];
29728     var front = null;
29729
29730     // private
29731     var sortDialogs = function(d1, d2){
29732         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29733     };
29734
29735     // private
29736     var orderDialogs = function(){
29737         accessList.sort(sortDialogs);
29738         var seed = Roo.DialogManager.zseed;
29739         for(var i = 0, len = accessList.length; i < len; i++){
29740             var dlg = accessList[i];
29741             if(dlg){
29742                 dlg.setZIndex(seed + (i*10));
29743             }
29744         }
29745     };
29746
29747     return {
29748         /**
29749          * The starting z-index for BasicDialogs (defaults to 9000)
29750          * @type Number The z-index value
29751          */
29752         zseed : 9000,
29753
29754         // private
29755         register : function(dlg){
29756             list[dlg.id] = dlg;
29757             accessList.push(dlg);
29758         },
29759
29760         // private
29761         unregister : function(dlg){
29762             delete list[dlg.id];
29763             var i=0;
29764             var len=0;
29765             if(!accessList.indexOf){
29766                 for(  i = 0, len = accessList.length; i < len; i++){
29767                     if(accessList[i] == dlg){
29768                         accessList.splice(i, 1);
29769                         return;
29770                     }
29771                 }
29772             }else{
29773                  i = accessList.indexOf(dlg);
29774                 if(i != -1){
29775                     accessList.splice(i, 1);
29776                 }
29777             }
29778         },
29779
29780         /**
29781          * Gets a registered dialog by id
29782          * @param {String/Object} id The id of the dialog or a dialog
29783          * @return {Roo.BasicDialog} this
29784          */
29785         get : function(id){
29786             return typeof id == "object" ? id : list[id];
29787         },
29788
29789         /**
29790          * Brings the specified dialog to the front
29791          * @param {String/Object} dlg The id of the dialog or a dialog
29792          * @return {Roo.BasicDialog} this
29793          */
29794         bringToFront : function(dlg){
29795             dlg = this.get(dlg);
29796             if(dlg != front){
29797                 front = dlg;
29798                 dlg._lastAccess = new Date().getTime();
29799                 orderDialogs();
29800             }
29801             return dlg;
29802         },
29803
29804         /**
29805          * Sends the specified dialog to the back
29806          * @param {String/Object} dlg The id of the dialog or a dialog
29807          * @return {Roo.BasicDialog} this
29808          */
29809         sendToBack : function(dlg){
29810             dlg = this.get(dlg);
29811             dlg._lastAccess = -(new Date().getTime());
29812             orderDialogs();
29813             return dlg;
29814         },
29815
29816         /**
29817          * Hides all dialogs
29818          */
29819         hideAll : function(){
29820             for(var id in list){
29821                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29822                     list[id].hide();
29823                 }
29824             }
29825         }
29826     };
29827 }();
29828
29829 /**
29830  * @class Roo.LayoutDialog
29831  * @extends Roo.BasicDialog
29832  * Dialog which provides adjustments for working with a layout in a Dialog.
29833  * Add your necessary layout config options to the dialog's config.<br>
29834  * Example usage (including a nested layout):
29835  * <pre><code>
29836 if(!dialog){
29837     dialog = new Roo.LayoutDialog("download-dlg", {
29838         modal: true,
29839         width:600,
29840         height:450,
29841         shadow:true,
29842         minWidth:500,
29843         minHeight:350,
29844         autoTabs:true,
29845         proxyDrag:true,
29846         // layout config merges with the dialog config
29847         center:{
29848             tabPosition: "top",
29849             alwaysShowTabs: true
29850         }
29851     });
29852     dialog.addKeyListener(27, dialog.hide, dialog);
29853     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29854     dialog.addButton("Build It!", this.getDownload, this);
29855
29856     // we can even add nested layouts
29857     var innerLayout = new Roo.BorderLayout("dl-inner", {
29858         east: {
29859             initialSize: 200,
29860             autoScroll:true,
29861             split:true
29862         },
29863         center: {
29864             autoScroll:true
29865         }
29866     });
29867     innerLayout.beginUpdate();
29868     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29869     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29870     innerLayout.endUpdate(true);
29871
29872     var layout = dialog.getLayout();
29873     layout.beginUpdate();
29874     layout.add("center", new Roo.ContentPanel("standard-panel",
29875                         {title: "Download the Source", fitToFrame:true}));
29876     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29877                {title: "Build your own roo.js"}));
29878     layout.getRegion("center").showPanel(sp);
29879     layout.endUpdate();
29880 }
29881 </code></pre>
29882     * @constructor
29883     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29884     * @param {Object} config configuration options
29885   */
29886 Roo.LayoutDialog = function(el, cfg){
29887     
29888     var config=  cfg;
29889     if (typeof(cfg) == 'undefined') {
29890         config = Roo.apply({}, el);
29891         // not sure why we use documentElement here.. - it should always be body.
29892         // IE7 borks horribly if we use documentElement.
29893         // webkit also does not like documentElement - it creates a body element...
29894         el = Roo.get( document.body || document.documentElement ).createChild();
29895         //config.autoCreate = true;
29896     }
29897     
29898     
29899     config.autoTabs = false;
29900     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29901     this.body.setStyle({overflow:"hidden", position:"relative"});
29902     this.layout = new Roo.BorderLayout(this.body.dom, config);
29903     this.layout.monitorWindowResize = false;
29904     this.el.addClass("x-dlg-auto-layout");
29905     // fix case when center region overwrites center function
29906     this.center = Roo.BasicDialog.prototype.center;
29907     this.on("show", this.layout.layout, this.layout, true);
29908     if (config.items) {
29909         var xitems = config.items;
29910         delete config.items;
29911         Roo.each(xitems, this.addxtype, this);
29912     }
29913     
29914     
29915 };
29916 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29917     /**
29918      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29919      * @deprecated
29920      */
29921     endUpdate : function(){
29922         this.layout.endUpdate();
29923     },
29924
29925     /**
29926      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29927      *  @deprecated
29928      */
29929     beginUpdate : function(){
29930         this.layout.beginUpdate();
29931     },
29932
29933     /**
29934      * Get the BorderLayout for this dialog
29935      * @return {Roo.BorderLayout}
29936      */
29937     getLayout : function(){
29938         return this.layout;
29939     },
29940
29941     showEl : function(){
29942         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29943         if(Roo.isIE7){
29944             this.layout.layout();
29945         }
29946     },
29947
29948     // private
29949     // Use the syncHeightBeforeShow config option to control this automatically
29950     syncBodyHeight : function(){
29951         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29952         if(this.layout){this.layout.layout();}
29953     },
29954     
29955       /**
29956      * Add an xtype element (actually adds to the layout.)
29957      * @return {Object} xdata xtype object data.
29958      */
29959     
29960     addxtype : function(c) {
29961         return this.layout.addxtype(c);
29962     }
29963 });/*
29964  * Based on:
29965  * Ext JS Library 1.1.1
29966  * Copyright(c) 2006-2007, Ext JS, LLC.
29967  *
29968  * Originally Released Under LGPL - original licence link has changed is not relivant.
29969  *
29970  * Fork - LGPL
29971  * <script type="text/javascript">
29972  */
29973  
29974 /**
29975  * @class Roo.MessageBox
29976  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29977  * Example usage:
29978  *<pre><code>
29979 // Basic alert:
29980 Roo.Msg.alert('Status', 'Changes saved successfully.');
29981
29982 // Prompt for user data:
29983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29984     if (btn == 'ok'){
29985         // process text value...
29986     }
29987 });
29988
29989 // Show a dialog using config options:
29990 Roo.Msg.show({
29991    title:'Save Changes?',
29992    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29993    buttons: Roo.Msg.YESNOCANCEL,
29994    fn: processResult,
29995    animEl: 'elId'
29996 });
29997 </code></pre>
29998  * @singleton
29999  */
30000 Roo.MessageBox = function(){
30001     var dlg, opt, mask, waitTimer;
30002     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30003     var buttons, activeTextEl, bwidth;
30004
30005     // private
30006     var handleButton = function(button){
30007         dlg.hide();
30008         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30009     };
30010
30011     // private
30012     var handleHide = function(){
30013         if(opt && opt.cls){
30014             dlg.el.removeClass(opt.cls);
30015         }
30016         if(waitTimer){
30017             Roo.TaskMgr.stop(waitTimer);
30018             waitTimer = null;
30019         }
30020     };
30021
30022     // private
30023     var updateButtons = function(b){
30024         var width = 0;
30025         if(!b){
30026             buttons["ok"].hide();
30027             buttons["cancel"].hide();
30028             buttons["yes"].hide();
30029             buttons["no"].hide();
30030             dlg.footer.dom.style.display = 'none';
30031             return width;
30032         }
30033         dlg.footer.dom.style.display = '';
30034         for(var k in buttons){
30035             if(typeof buttons[k] != "function"){
30036                 if(b[k]){
30037                     buttons[k].show();
30038                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30039                     width += buttons[k].el.getWidth()+15;
30040                 }else{
30041                     buttons[k].hide();
30042                 }
30043             }
30044         }
30045         return width;
30046     };
30047
30048     // private
30049     var handleEsc = function(d, k, e){
30050         if(opt && opt.closable !== false){
30051             dlg.hide();
30052         }
30053         if(e){
30054             e.stopEvent();
30055         }
30056     };
30057
30058     return {
30059         /**
30060          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30061          * @return {Roo.BasicDialog} The BasicDialog element
30062          */
30063         getDialog : function(){
30064            if(!dlg){
30065                 dlg = new Roo.BasicDialog("x-msg-box", {
30066                     autoCreate : true,
30067                     shadow: true,
30068                     draggable: true,
30069                     resizable:false,
30070                     constraintoviewport:false,
30071                     fixedcenter:true,
30072                     collapsible : false,
30073                     shim:true,
30074                     modal: true,
30075                     width:400, height:100,
30076                     buttonAlign:"center",
30077                     closeClick : function(){
30078                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30079                             handleButton("no");
30080                         }else{
30081                             handleButton("cancel");
30082                         }
30083                     }
30084                 });
30085                 dlg.on("hide", handleHide);
30086                 mask = dlg.mask;
30087                 dlg.addKeyListener(27, handleEsc);
30088                 buttons = {};
30089                 var bt = this.buttonText;
30090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30094                 bodyEl = dlg.body.createChild({
30095
30096                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
30097                 });
30098                 msgEl = bodyEl.dom.firstChild;
30099                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30100                 textboxEl.enableDisplayMode();
30101                 textboxEl.addKeyListener([10,13], function(){
30102                     if(dlg.isVisible() && opt && opt.buttons){
30103                         if(opt.buttons.ok){
30104                             handleButton("ok");
30105                         }else if(opt.buttons.yes){
30106                             handleButton("yes");
30107                         }
30108                     }
30109                 });
30110                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30111                 textareaEl.enableDisplayMode();
30112                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30113                 progressEl.enableDisplayMode();
30114                 var pf = progressEl.dom.firstChild;
30115                 if (pf) {
30116                     pp = Roo.get(pf.firstChild);
30117                     pp.setHeight(pf.offsetHeight);
30118                 }
30119                 
30120             }
30121             return dlg;
30122         },
30123
30124         /**
30125          * Updates the message box body text
30126          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30127          * the XHTML-compliant non-breaking space character '&amp;#160;')
30128          * @return {Roo.MessageBox} This message box
30129          */
30130         updateText : function(text){
30131             if(!dlg.isVisible() && !opt.width){
30132                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30133             }
30134             msgEl.innerHTML = text || '&#160;';
30135             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30136                         Math.max(opt.minWidth || this.minWidth, bwidth));
30137             if(opt.prompt){
30138                 activeTextEl.setWidth(w);
30139             }
30140             if(dlg.isVisible()){
30141                 dlg.fixedcenter = false;
30142             }
30143             dlg.setContentSize(w, bodyEl.getHeight());
30144             if(dlg.isVisible()){
30145                 dlg.fixedcenter = true;
30146             }
30147             return this;
30148         },
30149
30150         /**
30151          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30152          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30153          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30154          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30155          * @return {Roo.MessageBox} This message box
30156          */
30157         updateProgress : function(value, text){
30158             if(text){
30159                 this.updateText(text);
30160             }
30161             if (pp) { // weird bug on my firefox - for some reason this is not defined
30162                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30163             }
30164             return this;
30165         },        
30166
30167         /**
30168          * Returns true if the message box is currently displayed
30169          * @return {Boolean} True if the message box is visible, else false
30170          */
30171         isVisible : function(){
30172             return dlg && dlg.isVisible();  
30173         },
30174
30175         /**
30176          * Hides the message box if it is displayed
30177          */
30178         hide : function(){
30179             if(this.isVisible()){
30180                 dlg.hide();
30181             }  
30182         },
30183
30184         /**
30185          * Displays a new message box, or reinitializes an existing message box, based on the config options
30186          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30187          * The following config object properties are supported:
30188          * <pre>
30189 Property    Type             Description
30190 ----------  ---------------  ------------------------------------------------------------------------------------
30191 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30192                                    closes (defaults to undefined)
30193 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30194                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30195 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30196                                    progress and wait dialogs will ignore this property and always hide the
30197                                    close button as they can only be closed programmatically.
30198 cls               String           A custom CSS class to apply to the message box element
30199 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30200                                    displayed (defaults to 75)
30201 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30202                                    function will be btn (the name of the button that was clicked, if applicable,
30203                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30204                                    Progress and wait dialogs will ignore this option since they do not respond to
30205                                    user actions and can only be closed programmatically, so any required function
30206                                    should be called by the same code after it closes the dialog.
30207 icon              String           A CSS class that provides a background image to be used as an icon for
30208                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30209 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30210 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30211 modal             Boolean          False to allow user interaction with the page while the message box is
30212                                    displayed (defaults to true)
30213 msg               String           A string that will replace the existing message box body text (defaults
30214                                    to the XHTML-compliant non-breaking space character '&#160;')
30215 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30216 progress          Boolean          True to display a progress bar (defaults to false)
30217 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30218 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30219 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30220 title             String           The title text
30221 value             String           The string value to set into the active textbox element if displayed
30222 wait              Boolean          True to display a progress bar (defaults to false)
30223 width             Number           The width of the dialog in pixels
30224 </pre>
30225          *
30226          * Example usage:
30227          * <pre><code>
30228 Roo.Msg.show({
30229    title: 'Address',
30230    msg: 'Please enter your address:',
30231    width: 300,
30232    buttons: Roo.MessageBox.OKCANCEL,
30233    multiline: true,
30234    fn: saveAddress,
30235    animEl: 'addAddressBtn'
30236 });
30237 </code></pre>
30238          * @param {Object} config Configuration options
30239          * @return {Roo.MessageBox} This message box
30240          */
30241         show : function(options)
30242         {
30243             
30244             // this causes nightmares if you show one dialog after another
30245             // especially on callbacks..
30246              
30247             if(this.isVisible()){
30248                 
30249                 this.hide();
30250                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30253                 
30254             }
30255             var d = this.getDialog();
30256             opt = options;
30257             d.setTitle(opt.title || "&#160;");
30258             d.close.setDisplayed(opt.closable !== false);
30259             activeTextEl = textboxEl;
30260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30261             if(opt.prompt){
30262                 if(opt.multiline){
30263                     textboxEl.hide();
30264                     textareaEl.show();
30265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30266                         opt.multiline : this.defaultTextHeight);
30267                     activeTextEl = textareaEl;
30268                 }else{
30269                     textboxEl.show();
30270                     textareaEl.hide();
30271                 }
30272             }else{
30273                 textboxEl.hide();
30274                 textareaEl.hide();
30275             }
30276             progressEl.setDisplayed(opt.progress === true);
30277             this.updateProgress(0);
30278             activeTextEl.dom.value = opt.value || "";
30279             if(opt.prompt){
30280                 dlg.setDefaultButton(activeTextEl);
30281             }else{
30282                 var bs = opt.buttons;
30283                 var db = null;
30284                 if(bs && bs.ok){
30285                     db = buttons["ok"];
30286                 }else if(bs && bs.yes){
30287                     db = buttons["yes"];
30288                 }
30289                 dlg.setDefaultButton(db);
30290             }
30291             bwidth = updateButtons(opt.buttons);
30292             this.updateText(opt.msg);
30293             if(opt.cls){
30294                 d.el.addClass(opt.cls);
30295             }
30296             d.proxyDrag = opt.proxyDrag === true;
30297             d.modal = opt.modal !== false;
30298             d.mask = opt.modal !== false ? mask : false;
30299             if(!d.isVisible()){
30300                 // force it to the end of the z-index stack so it gets a cursor in FF
30301                 document.body.appendChild(dlg.el.dom);
30302                 d.animateTarget = null;
30303                 d.show(options.animEl);
30304             }
30305             return this;
30306         },
30307
30308         /**
30309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30311          * and closing the message box when the process is complete.
30312          * @param {String} title The title bar text
30313          * @param {String} msg The message box body text
30314          * @return {Roo.MessageBox} This message box
30315          */
30316         progress : function(title, msg){
30317             this.show({
30318                 title : title,
30319                 msg : msg,
30320                 buttons: false,
30321                 progress:true,
30322                 closable:false,
30323                 minWidth: this.minProgressWidth,
30324                 modal : true
30325             });
30326             return this;
30327         },
30328
30329         /**
30330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30331          * If a callback function is passed it will be called after the user clicks the button, and the
30332          * id of the button that was clicked will be passed as the only parameter to the callback
30333          * (could also be the top-right close button).
30334          * @param {String} title The title bar text
30335          * @param {String} msg The message box body text
30336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30337          * @param {Object} scope (optional) The scope of the callback function
30338          * @return {Roo.MessageBox} This message box
30339          */
30340         alert : function(title, msg, fn, scope){
30341             this.show({
30342                 title : title,
30343                 msg : msg,
30344                 buttons: this.OK,
30345                 fn: fn,
30346                 scope : scope,
30347                 modal : true
30348             });
30349             return this;
30350         },
30351
30352         /**
30353          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30354          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30355          * You are responsible for closing the message box when the process is complete.
30356          * @param {String} msg The message box body text
30357          * @param {String} title (optional) The title bar text
30358          * @return {Roo.MessageBox} This message box
30359          */
30360         wait : function(msg, title){
30361             this.show({
30362                 title : title,
30363                 msg : msg,
30364                 buttons: false,
30365                 closable:false,
30366                 progress:true,
30367                 modal:true,
30368                 width:300,
30369                 wait:true
30370             });
30371             waitTimer = Roo.TaskMgr.start({
30372                 run: function(i){
30373                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30374                 },
30375                 interval: 1000
30376             });
30377             return this;
30378         },
30379
30380         /**
30381          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30382          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30383          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30384          * @param {String} title The title bar text
30385          * @param {String} msg The message box body text
30386          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30387          * @param {Object} scope (optional) The scope of the callback function
30388          * @return {Roo.MessageBox} This message box
30389          */
30390         confirm : function(title, msg, fn, scope){
30391             this.show({
30392                 title : title,
30393                 msg : msg,
30394                 buttons: this.YESNO,
30395                 fn: fn,
30396                 scope : scope,
30397                 modal : true
30398             });
30399             return this;
30400         },
30401
30402         /**
30403          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30404          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30405          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30406          * (could also be the top-right close button) and the text that was entered will be passed as the two
30407          * parameters to the callback.
30408          * @param {String} title The title bar text
30409          * @param {String} msg The message box body text
30410          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30411          * @param {Object} scope (optional) The scope of the callback function
30412          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30413          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30414          * @return {Roo.MessageBox} This message box
30415          */
30416         prompt : function(title, msg, fn, scope, multiline){
30417             this.show({
30418                 title : title,
30419                 msg : msg,
30420                 buttons: this.OKCANCEL,
30421                 fn: fn,
30422                 minWidth:250,
30423                 scope : scope,
30424                 prompt:true,
30425                 multiline: multiline,
30426                 modal : true
30427             });
30428             return this;
30429         },
30430
30431         /**
30432          * Button config that displays a single OK button
30433          * @type Object
30434          */
30435         OK : {ok:true},
30436         /**
30437          * Button config that displays Yes and No buttons
30438          * @type Object
30439          */
30440         YESNO : {yes:true, no:true},
30441         /**
30442          * Button config that displays OK and Cancel buttons
30443          * @type Object
30444          */
30445         OKCANCEL : {ok:true, cancel:true},
30446         /**
30447          * Button config that displays Yes, No and Cancel buttons
30448          * @type Object
30449          */
30450         YESNOCANCEL : {yes:true, no:true, cancel:true},
30451
30452         /**
30453          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30454          * @type Number
30455          */
30456         defaultTextHeight : 75,
30457         /**
30458          * The maximum width in pixels of the message box (defaults to 600)
30459          * @type Number
30460          */
30461         maxWidth : 600,
30462         /**
30463          * The minimum width in pixels of the message box (defaults to 100)
30464          * @type Number
30465          */
30466         minWidth : 100,
30467         /**
30468          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30469          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30470          * @type Number
30471          */
30472         minProgressWidth : 250,
30473         /**
30474          * An object containing the default button text strings that can be overriden for localized language support.
30475          * Supported properties are: ok, cancel, yes and no.
30476          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30477          * @type Object
30478          */
30479         buttonText : {
30480             ok : "OK",
30481             cancel : "Cancel",
30482             yes : "Yes",
30483             no : "No"
30484         }
30485     };
30486 }();
30487
30488 /**
30489  * Shorthand for {@link Roo.MessageBox}
30490  */
30491 Roo.Msg = Roo.MessageBox;/*
30492  * Based on:
30493  * Ext JS Library 1.1.1
30494  * Copyright(c) 2006-2007, Ext JS, LLC.
30495  *
30496  * Originally Released Under LGPL - original licence link has changed is not relivant.
30497  *
30498  * Fork - LGPL
30499  * <script type="text/javascript">
30500  */
30501 /**
30502  * @class Roo.QuickTips
30503  * Provides attractive and customizable tooltips for any element.
30504  * @singleton
30505  */
30506 Roo.QuickTips = function(){
30507     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30508     var ce, bd, xy, dd;
30509     var visible = false, disabled = true, inited = false;
30510     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30511     
30512     var onOver = function(e){
30513         if(disabled){
30514             return;
30515         }
30516         var t = e.getTarget();
30517         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30518             return;
30519         }
30520         if(ce && t == ce.el){
30521             clearTimeout(hideProc);
30522             return;
30523         }
30524         if(t && tagEls[t.id]){
30525             tagEls[t.id].el = t;
30526             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30527             return;
30528         }
30529         var ttp, et = Roo.fly(t);
30530         var ns = cfg.namespace;
30531         if(tm.interceptTitles && t.title){
30532             ttp = t.title;
30533             t.qtip = ttp;
30534             t.removeAttribute("title");
30535             e.preventDefault();
30536         }else{
30537             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30538         }
30539         if(ttp){
30540             showProc = show.defer(tm.showDelay, tm, [{
30541                 el: t, 
30542                 text: ttp, 
30543                 width: et.getAttributeNS(ns, cfg.width),
30544                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30545                 title: et.getAttributeNS(ns, cfg.title),
30546                     cls: et.getAttributeNS(ns, cfg.cls)
30547             }]);
30548         }
30549     };
30550     
30551     var onOut = function(e){
30552         clearTimeout(showProc);
30553         var t = e.getTarget();
30554         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30555             hideProc = setTimeout(hide, tm.hideDelay);
30556         }
30557     };
30558     
30559     var onMove = function(e){
30560         if(disabled){
30561             return;
30562         }
30563         xy = e.getXY();
30564         xy[1] += 18;
30565         if(tm.trackMouse && ce){
30566             el.setXY(xy);
30567         }
30568     };
30569     
30570     var onDown = function(e){
30571         clearTimeout(showProc);
30572         clearTimeout(hideProc);
30573         if(!e.within(el)){
30574             if(tm.hideOnClick){
30575                 hide();
30576                 tm.disable();
30577                 tm.enable.defer(100, tm);
30578             }
30579         }
30580     };
30581     
30582     var getPad = function(){
30583         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30584     };
30585
30586     var show = function(o){
30587         if(disabled){
30588             return;
30589         }
30590         clearTimeout(dismissProc);
30591         ce = o;
30592         if(removeCls){ // in case manually hidden
30593             el.removeClass(removeCls);
30594             removeCls = null;
30595         }
30596         if(ce.cls){
30597             el.addClass(ce.cls);
30598             removeCls = ce.cls;
30599         }
30600         if(ce.title){
30601             tipTitle.update(ce.title);
30602             tipTitle.show();
30603         }else{
30604             tipTitle.update('');
30605             tipTitle.hide();
30606         }
30607         el.dom.style.width  = tm.maxWidth+'px';
30608         //tipBody.dom.style.width = '';
30609         tipBodyText.update(o.text);
30610         var p = getPad(), w = ce.width;
30611         if(!w){
30612             var td = tipBodyText.dom;
30613             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30614             if(aw > tm.maxWidth){
30615                 w = tm.maxWidth;
30616             }else if(aw < tm.minWidth){
30617                 w = tm.minWidth;
30618             }else{
30619                 w = aw;
30620             }
30621         }
30622         //tipBody.setWidth(w);
30623         el.setWidth(parseInt(w, 10) + p);
30624         if(ce.autoHide === false){
30625             close.setDisplayed(true);
30626             if(dd){
30627                 dd.unlock();
30628             }
30629         }else{
30630             close.setDisplayed(false);
30631             if(dd){
30632                 dd.lock();
30633             }
30634         }
30635         if(xy){
30636             el.avoidY = xy[1]-18;
30637             el.setXY(xy);
30638         }
30639         if(tm.animate){
30640             el.setOpacity(.1);
30641             el.setStyle("visibility", "visible");
30642             el.fadeIn({callback: afterShow});
30643         }else{
30644             afterShow();
30645         }
30646     };
30647     
30648     var afterShow = function(){
30649         if(ce){
30650             el.show();
30651             esc.enable();
30652             if(tm.autoDismiss && ce.autoHide !== false){
30653                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30654             }
30655         }
30656     };
30657     
30658     var hide = function(noanim){
30659         clearTimeout(dismissProc);
30660         clearTimeout(hideProc);
30661         ce = null;
30662         if(el.isVisible()){
30663             esc.disable();
30664             if(noanim !== true && tm.animate){
30665                 el.fadeOut({callback: afterHide});
30666             }else{
30667                 afterHide();
30668             } 
30669         }
30670     };
30671     
30672     var afterHide = function(){
30673         el.hide();
30674         if(removeCls){
30675             el.removeClass(removeCls);
30676             removeCls = null;
30677         }
30678     };
30679     
30680     return {
30681         /**
30682         * @cfg {Number} minWidth
30683         * The minimum width of the quick tip (defaults to 40)
30684         */
30685        minWidth : 40,
30686         /**
30687         * @cfg {Number} maxWidth
30688         * The maximum width of the quick tip (defaults to 300)
30689         */
30690        maxWidth : 300,
30691         /**
30692         * @cfg {Boolean} interceptTitles
30693         * True to automatically use the element's DOM title value if available (defaults to false)
30694         */
30695        interceptTitles : false,
30696         /**
30697         * @cfg {Boolean} trackMouse
30698         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30699         */
30700        trackMouse : false,
30701         /**
30702         * @cfg {Boolean} hideOnClick
30703         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30704         */
30705        hideOnClick : true,
30706         /**
30707         * @cfg {Number} showDelay
30708         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30709         */
30710        showDelay : 500,
30711         /**
30712         * @cfg {Number} hideDelay
30713         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30714         */
30715        hideDelay : 200,
30716         /**
30717         * @cfg {Boolean} autoHide
30718         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30719         * Used in conjunction with hideDelay.
30720         */
30721        autoHide : true,
30722         /**
30723         * @cfg {Boolean}
30724         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30725         * (defaults to true).  Used in conjunction with autoDismissDelay.
30726         */
30727        autoDismiss : true,
30728         /**
30729         * @cfg {Number}
30730         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30731         */
30732        autoDismissDelay : 5000,
30733        /**
30734         * @cfg {Boolean} animate
30735         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30736         */
30737        animate : false,
30738
30739        /**
30740         * @cfg {String} title
30741         * Title text to display (defaults to '').  This can be any valid HTML markup.
30742         */
30743         title: '',
30744        /**
30745         * @cfg {String} text
30746         * Body text to display (defaults to '').  This can be any valid HTML markup.
30747         */
30748         text : '',
30749        /**
30750         * @cfg {String} cls
30751         * A CSS class to apply to the base quick tip element (defaults to '').
30752         */
30753         cls : '',
30754        /**
30755         * @cfg {Number} width
30756         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30757         * minWidth or maxWidth.
30758         */
30759         width : null,
30760
30761     /**
30762      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30763      * or display QuickTips in a page.
30764      */
30765        init : function(){
30766           tm = Roo.QuickTips;
30767           cfg = tm.tagConfig;
30768           if(!inited){
30769               if(!Roo.isReady){ // allow calling of init() before onReady
30770                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30771                   return;
30772               }
30773               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30774               el.fxDefaults = {stopFx: true};
30775               // maximum custom styling
30776               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30777               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30778               tipTitle = el.child('h3');
30779               tipTitle.enableDisplayMode("block");
30780               tipBody = el.child('div.x-tip-bd');
30781               tipBodyText = el.child('div.x-tip-bd-inner');
30782               //bdLeft = el.child('div.x-tip-bd-left');
30783               //bdRight = el.child('div.x-tip-bd-right');
30784               close = el.child('div.x-tip-close');
30785               close.enableDisplayMode("block");
30786               close.on("click", hide);
30787               var d = Roo.get(document);
30788               d.on("mousedown", onDown);
30789               d.on("mouseover", onOver);
30790               d.on("mouseout", onOut);
30791               d.on("mousemove", onMove);
30792               esc = d.addKeyListener(27, hide);
30793               esc.disable();
30794               if(Roo.dd.DD){
30795                   dd = el.initDD("default", null, {
30796                       onDrag : function(){
30797                           el.sync();  
30798                       }
30799                   });
30800                   dd.setHandleElId(tipTitle.id);
30801                   dd.lock();
30802               }
30803               inited = true;
30804           }
30805           this.enable(); 
30806        },
30807
30808     /**
30809      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30810      * are supported:
30811      * <pre>
30812 Property    Type                   Description
30813 ----------  ---------------------  ------------------------------------------------------------------------
30814 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30815      * </ul>
30816      * @param {Object} config The config object
30817      */
30818        register : function(config){
30819            var cs = config instanceof Array ? config : arguments;
30820            for(var i = 0, len = cs.length; i < len; i++) {
30821                var c = cs[i];
30822                var target = c.target;
30823                if(target){
30824                    if(target instanceof Array){
30825                        for(var j = 0, jlen = target.length; j < jlen; j++){
30826                            tagEls[target[j]] = c;
30827                        }
30828                    }else{
30829                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30830                    }
30831                }
30832            }
30833        },
30834
30835     /**
30836      * Removes this quick tip from its element and destroys it.
30837      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30838      */
30839        unregister : function(el){
30840            delete tagEls[Roo.id(el)];
30841        },
30842
30843     /**
30844      * Enable this quick tip.
30845      */
30846        enable : function(){
30847            if(inited && disabled){
30848                locks.pop();
30849                if(locks.length < 1){
30850                    disabled = false;
30851                }
30852            }
30853        },
30854
30855     /**
30856      * Disable this quick tip.
30857      */
30858        disable : function(){
30859           disabled = true;
30860           clearTimeout(showProc);
30861           clearTimeout(hideProc);
30862           clearTimeout(dismissProc);
30863           if(ce){
30864               hide(true);
30865           }
30866           locks.push(1);
30867        },
30868
30869     /**
30870      * Returns true if the quick tip is enabled, else false.
30871      */
30872        isEnabled : function(){
30873             return !disabled;
30874        },
30875
30876         // private
30877        tagConfig : {
30878            namespace : "ext",
30879            attribute : "qtip",
30880            width : "width",
30881            target : "target",
30882            title : "qtitle",
30883            hide : "hide",
30884            cls : "qclass"
30885        }
30886    };
30887 }();
30888
30889 // backwards compat
30890 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30891  * Based on:
30892  * Ext JS Library 1.1.1
30893  * Copyright(c) 2006-2007, Ext JS, LLC.
30894  *
30895  * Originally Released Under LGPL - original licence link has changed is not relivant.
30896  *
30897  * Fork - LGPL
30898  * <script type="text/javascript">
30899  */
30900  
30901
30902 /**
30903  * @class Roo.tree.TreePanel
30904  * @extends Roo.data.Tree
30905
30906  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30907  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30908  * @cfg {Boolean} enableDD true to enable drag and drop
30909  * @cfg {Boolean} enableDrag true to enable just drag
30910  * @cfg {Boolean} enableDrop true to enable just drop
30911  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30912  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30913  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30914  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30915  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30916  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30917  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30918  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30919  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30920  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30921  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30922  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30923  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30924  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30925  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30926  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30927  * 
30928  * @constructor
30929  * @param {String/HTMLElement/Element} el The container element
30930  * @param {Object} config
30931  */
30932 Roo.tree.TreePanel = function(el, config){
30933     var root = false;
30934     var loader = false;
30935     if (config.root) {
30936         root = config.root;
30937         delete config.root;
30938     }
30939     if (config.loader) {
30940         loader = config.loader;
30941         delete config.loader;
30942     }
30943     
30944     Roo.apply(this, config);
30945     Roo.tree.TreePanel.superclass.constructor.call(this);
30946     this.el = Roo.get(el);
30947     this.el.addClass('x-tree');
30948     //console.log(root);
30949     if (root) {
30950         this.setRootNode( Roo.factory(root, Roo.tree));
30951     }
30952     if (loader) {
30953         this.loader = Roo.factory(loader, Roo.tree);
30954     }
30955    /**
30956     * Read-only. The id of the container element becomes this TreePanel's id.
30957     */
30958     this.id = this.el.id;
30959     this.addEvents({
30960         /**
30961         * @event beforeload
30962         * Fires before a node is loaded, return false to cancel
30963         * @param {Node} node The node being loaded
30964         */
30965         "beforeload" : true,
30966         /**
30967         * @event load
30968         * Fires when a node is loaded
30969         * @param {Node} node The node that was loaded
30970         */
30971         "load" : true,
30972         /**
30973         * @event textchange
30974         * Fires when the text for a node is changed
30975         * @param {Node} node The node
30976         * @param {String} text The new text
30977         * @param {String} oldText The old text
30978         */
30979         "textchange" : true,
30980         /**
30981         * @event beforeexpand
30982         * Fires before a node is expanded, return false to cancel.
30983         * @param {Node} node The node
30984         * @param {Boolean} deep
30985         * @param {Boolean} anim
30986         */
30987         "beforeexpand" : true,
30988         /**
30989         * @event beforecollapse
30990         * Fires before a node is collapsed, return false to cancel.
30991         * @param {Node} node The node
30992         * @param {Boolean} deep
30993         * @param {Boolean} anim
30994         */
30995         "beforecollapse" : true,
30996         /**
30997         * @event expand
30998         * Fires when a node is expanded
30999         * @param {Node} node The node
31000         */
31001         "expand" : true,
31002         /**
31003         * @event disabledchange
31004         * Fires when the disabled status of a node changes
31005         * @param {Node} node The node
31006         * @param {Boolean} disabled
31007         */
31008         "disabledchange" : true,
31009         /**
31010         * @event collapse
31011         * Fires when a node is collapsed
31012         * @param {Node} node The node
31013         */
31014         "collapse" : true,
31015         /**
31016         * @event beforeclick
31017         * Fires before click processing on a node. Return false to cancel the default action.
31018         * @param {Node} node The node
31019         * @param {Roo.EventObject} e The event object
31020         */
31021         "beforeclick":true,
31022         /**
31023         * @event checkchange
31024         * Fires when a node with a checkbox's checked property changes
31025         * @param {Node} this This node
31026         * @param {Boolean} checked
31027         */
31028         "checkchange":true,
31029         /**
31030         * @event click
31031         * Fires when a node is clicked
31032         * @param {Node} node The node
31033         * @param {Roo.EventObject} e The event object
31034         */
31035         "click":true,
31036         /**
31037         * @event dblclick
31038         * Fires when a node is double clicked
31039         * @param {Node} node The node
31040         * @param {Roo.EventObject} e The event object
31041         */
31042         "dblclick":true,
31043         /**
31044         * @event contextmenu
31045         * Fires when a node is right clicked
31046         * @param {Node} node The node
31047         * @param {Roo.EventObject} e The event object
31048         */
31049         "contextmenu":true,
31050         /**
31051         * @event beforechildrenrendered
31052         * Fires right before the child nodes for a node are rendered
31053         * @param {Node} node The node
31054         */
31055         "beforechildrenrendered":true,
31056         /**
31057         * @event startdrag
31058         * Fires when a node starts being dragged
31059         * @param {Roo.tree.TreePanel} this
31060         * @param {Roo.tree.TreeNode} node
31061         * @param {event} e The raw browser event
31062         */ 
31063        "startdrag" : true,
31064        /**
31065         * @event enddrag
31066         * Fires when a drag operation is complete
31067         * @param {Roo.tree.TreePanel} this
31068         * @param {Roo.tree.TreeNode} node
31069         * @param {event} e The raw browser event
31070         */
31071        "enddrag" : true,
31072        /**
31073         * @event dragdrop
31074         * Fires when a dragged node is dropped on a valid DD target
31075         * @param {Roo.tree.TreePanel} this
31076         * @param {Roo.tree.TreeNode} node
31077         * @param {DD} dd The dd it was dropped on
31078         * @param {event} e The raw browser event
31079         */
31080        "dragdrop" : true,
31081        /**
31082         * @event beforenodedrop
31083         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31084         * passed to handlers has the following properties:<br />
31085         * <ul style="padding:5px;padding-left:16px;">
31086         * <li>tree - The TreePanel</li>
31087         * <li>target - The node being targeted for the drop</li>
31088         * <li>data - The drag data from the drag source</li>
31089         * <li>point - The point of the drop - append, above or below</li>
31090         * <li>source - The drag source</li>
31091         * <li>rawEvent - Raw mouse event</li>
31092         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31093         * to be inserted by setting them on this object.</li>
31094         * <li>cancel - Set this to true to cancel the drop.</li>
31095         * </ul>
31096         * @param {Object} dropEvent
31097         */
31098        "beforenodedrop" : true,
31099        /**
31100         * @event nodedrop
31101         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31102         * passed to handlers has the following properties:<br />
31103         * <ul style="padding:5px;padding-left:16px;">
31104         * <li>tree - The TreePanel</li>
31105         * <li>target - The node being targeted for the drop</li>
31106         * <li>data - The drag data from the drag source</li>
31107         * <li>point - The point of the drop - append, above or below</li>
31108         * <li>source - The drag source</li>
31109         * <li>rawEvent - Raw mouse event</li>
31110         * <li>dropNode - Dropped node(s).</li>
31111         * </ul>
31112         * @param {Object} dropEvent
31113         */
31114        "nodedrop" : true,
31115         /**
31116         * @event nodedragover
31117         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31118         * passed to handlers has the following properties:<br />
31119         * <ul style="padding:5px;padding-left:16px;">
31120         * <li>tree - The TreePanel</li>
31121         * <li>target - The node being targeted for the drop</li>
31122         * <li>data - The drag data from the drag source</li>
31123         * <li>point - The point of the drop - append, above or below</li>
31124         * <li>source - The drag source</li>
31125         * <li>rawEvent - Raw mouse event</li>
31126         * <li>dropNode - Drop node(s) provided by the source.</li>
31127         * <li>cancel - Set this to true to signal drop not allowed.</li>
31128         * </ul>
31129         * @param {Object} dragOverEvent
31130         */
31131        "nodedragover" : true
31132         
31133     });
31134     if(this.singleExpand){
31135        this.on("beforeexpand", this.restrictExpand, this);
31136     }
31137     if (this.editor) {
31138         this.editor.tree = this;
31139         this.editor = Roo.factory(this.editor, Roo.tree);
31140     }
31141     
31142     if (this.selModel) {
31143         this.selModel = Roo.factory(this.selModel, Roo.tree);
31144     }
31145    
31146 };
31147 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31148     rootVisible : true,
31149     animate: Roo.enableFx,
31150     lines : true,
31151     enableDD : false,
31152     hlDrop : Roo.enableFx,
31153   
31154     renderer: false,
31155     
31156     rendererTip: false,
31157     // private
31158     restrictExpand : function(node){
31159         var p = node.parentNode;
31160         if(p){
31161             if(p.expandedChild && p.expandedChild.parentNode == p){
31162                 p.expandedChild.collapse();
31163             }
31164             p.expandedChild = node;
31165         }
31166     },
31167
31168     // private override
31169     setRootNode : function(node){
31170         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31171         if(!this.rootVisible){
31172             node.ui = new Roo.tree.RootTreeNodeUI(node);
31173         }
31174         return node;
31175     },
31176
31177     /**
31178      * Returns the container element for this TreePanel
31179      */
31180     getEl : function(){
31181         return this.el;
31182     },
31183
31184     /**
31185      * Returns the default TreeLoader for this TreePanel
31186      */
31187     getLoader : function(){
31188         return this.loader;
31189     },
31190
31191     /**
31192      * Expand all nodes
31193      */
31194     expandAll : function(){
31195         this.root.expand(true);
31196     },
31197
31198     /**
31199      * Collapse all nodes
31200      */
31201     collapseAll : function(){
31202         this.root.collapse(true);
31203     },
31204
31205     /**
31206      * Returns the selection model used by this TreePanel
31207      */
31208     getSelectionModel : function(){
31209         if(!this.selModel){
31210             this.selModel = new Roo.tree.DefaultSelectionModel();
31211         }
31212         return this.selModel;
31213     },
31214
31215     /**
31216      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31217      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31218      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31219      * @return {Array}
31220      */
31221     getChecked : function(a, startNode){
31222         startNode = startNode || this.root;
31223         var r = [];
31224         var f = function(){
31225             if(this.attributes.checked){
31226                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31227             }
31228         }
31229         startNode.cascade(f);
31230         return r;
31231     },
31232
31233     /**
31234      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31235      * @param {String} path
31236      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31237      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31238      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31239      */
31240     expandPath : function(path, attr, callback){
31241         attr = attr || "id";
31242         var keys = path.split(this.pathSeparator);
31243         var curNode = this.root;
31244         if(curNode.attributes[attr] != keys[1]){ // invalid root
31245             if(callback){
31246                 callback(false, null);
31247             }
31248             return;
31249         }
31250         var index = 1;
31251         var f = function(){
31252             if(++index == keys.length){
31253                 if(callback){
31254                     callback(true, curNode);
31255                 }
31256                 return;
31257             }
31258             var c = curNode.findChild(attr, keys[index]);
31259             if(!c){
31260                 if(callback){
31261                     callback(false, curNode);
31262                 }
31263                 return;
31264             }
31265             curNode = c;
31266             c.expand(false, false, f);
31267         };
31268         curNode.expand(false, false, f);
31269     },
31270
31271     /**
31272      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31273      * @param {String} path
31274      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31275      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31276      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31277      */
31278     selectPath : function(path, attr, callback){
31279         attr = attr || "id";
31280         var keys = path.split(this.pathSeparator);
31281         var v = keys.pop();
31282         if(keys.length > 0){
31283             var f = function(success, node){
31284                 if(success && node){
31285                     var n = node.findChild(attr, v);
31286                     if(n){
31287                         n.select();
31288                         if(callback){
31289                             callback(true, n);
31290                         }
31291                     }else if(callback){
31292                         callback(false, n);
31293                     }
31294                 }else{
31295                     if(callback){
31296                         callback(false, n);
31297                     }
31298                 }
31299             };
31300             this.expandPath(keys.join(this.pathSeparator), attr, f);
31301         }else{
31302             this.root.select();
31303             if(callback){
31304                 callback(true, this.root);
31305             }
31306         }
31307     },
31308
31309     getTreeEl : function(){
31310         return this.el;
31311     },
31312
31313     /**
31314      * Trigger rendering of this TreePanel
31315      */
31316     render : function(){
31317         if (this.innerCt) {
31318             return this; // stop it rendering more than once!!
31319         }
31320         
31321         this.innerCt = this.el.createChild({tag:"ul",
31322                cls:"x-tree-root-ct " +
31323                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31324
31325         if(this.containerScroll){
31326             Roo.dd.ScrollManager.register(this.el);
31327         }
31328         if((this.enableDD || this.enableDrop) && !this.dropZone){
31329            /**
31330             * The dropZone used by this tree if drop is enabled
31331             * @type Roo.tree.TreeDropZone
31332             */
31333              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31334                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31335            });
31336         }
31337         if((this.enableDD || this.enableDrag) && !this.dragZone){
31338            /**
31339             * The dragZone used by this tree if drag is enabled
31340             * @type Roo.tree.TreeDragZone
31341             */
31342             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31343                ddGroup: this.ddGroup || "TreeDD",
31344                scroll: this.ddScroll
31345            });
31346         }
31347         this.getSelectionModel().init(this);
31348         if (!this.root) {
31349             console.log("ROOT not set in tree");
31350             return;
31351         }
31352         this.root.render();
31353         if(!this.rootVisible){
31354             this.root.renderChildren();
31355         }
31356         return this;
31357     }
31358 });/*
31359  * Based on:
31360  * Ext JS Library 1.1.1
31361  * Copyright(c) 2006-2007, Ext JS, LLC.
31362  *
31363  * Originally Released Under LGPL - original licence link has changed is not relivant.
31364  *
31365  * Fork - LGPL
31366  * <script type="text/javascript">
31367  */
31368  
31369
31370 /**
31371  * @class Roo.tree.DefaultSelectionModel
31372  * @extends Roo.util.Observable
31373  * The default single selection for a TreePanel.
31374  * @param {Object} cfg Configuration
31375  */
31376 Roo.tree.DefaultSelectionModel = function(cfg){
31377    this.selNode = null;
31378    
31379    
31380    
31381    this.addEvents({
31382        /**
31383         * @event selectionchange
31384         * Fires when the selected node changes
31385         * @param {DefaultSelectionModel} this
31386         * @param {TreeNode} node the new selection
31387         */
31388        "selectionchange" : true,
31389
31390        /**
31391         * @event beforeselect
31392         * Fires before the selected node changes, return false to cancel the change
31393         * @param {DefaultSelectionModel} this
31394         * @param {TreeNode} node the new selection
31395         * @param {TreeNode} node the old selection
31396         */
31397        "beforeselect" : true
31398    });
31399    
31400     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31401 };
31402
31403 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31404     init : function(tree){
31405         this.tree = tree;
31406         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31407         tree.on("click", this.onNodeClick, this);
31408     },
31409     
31410     onNodeClick : function(node, e){
31411         if (e.ctrlKey && this.selNode == node)  {
31412             this.unselect(node);
31413             return;
31414         }
31415         this.select(node);
31416     },
31417     
31418     /**
31419      * Select a node.
31420      * @param {TreeNode} node The node to select
31421      * @return {TreeNode} The selected node
31422      */
31423     select : function(node){
31424         var last = this.selNode;
31425         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31426             if(last){
31427                 last.ui.onSelectedChange(false);
31428             }
31429             this.selNode = node;
31430             node.ui.onSelectedChange(true);
31431             this.fireEvent("selectionchange", this, node, last);
31432         }
31433         return node;
31434     },
31435     
31436     /**
31437      * Deselect a node.
31438      * @param {TreeNode} node The node to unselect
31439      */
31440     unselect : function(node){
31441         if(this.selNode == node){
31442             this.clearSelections();
31443         }    
31444     },
31445     
31446     /**
31447      * Clear all selections
31448      */
31449     clearSelections : function(){
31450         var n = this.selNode;
31451         if(n){
31452             n.ui.onSelectedChange(false);
31453             this.selNode = null;
31454             this.fireEvent("selectionchange", this, null);
31455         }
31456         return n;
31457     },
31458     
31459     /**
31460      * Get the selected node
31461      * @return {TreeNode} The selected node
31462      */
31463     getSelectedNode : function(){
31464         return this.selNode;    
31465     },
31466     
31467     /**
31468      * Returns true if the node is selected
31469      * @param {TreeNode} node The node to check
31470      * @return {Boolean}
31471      */
31472     isSelected : function(node){
31473         return this.selNode == node;  
31474     },
31475
31476     /**
31477      * Selects the node above the selected node in the tree, intelligently walking the nodes
31478      * @return TreeNode The new selection
31479      */
31480     selectPrevious : function(){
31481         var s = this.selNode || this.lastSelNode;
31482         if(!s){
31483             return null;
31484         }
31485         var ps = s.previousSibling;
31486         if(ps){
31487             if(!ps.isExpanded() || ps.childNodes.length < 1){
31488                 return this.select(ps);
31489             } else{
31490                 var lc = ps.lastChild;
31491                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31492                     lc = lc.lastChild;
31493                 }
31494                 return this.select(lc);
31495             }
31496         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31497             return this.select(s.parentNode);
31498         }
31499         return null;
31500     },
31501
31502     /**
31503      * Selects the node above the selected node in the tree, intelligently walking the nodes
31504      * @return TreeNode The new selection
31505      */
31506     selectNext : function(){
31507         var s = this.selNode || this.lastSelNode;
31508         if(!s){
31509             return null;
31510         }
31511         if(s.firstChild && s.isExpanded()){
31512              return this.select(s.firstChild);
31513          }else if(s.nextSibling){
31514              return this.select(s.nextSibling);
31515          }else if(s.parentNode){
31516             var newS = null;
31517             s.parentNode.bubble(function(){
31518                 if(this.nextSibling){
31519                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31520                     return false;
31521                 }
31522             });
31523             return newS;
31524          }
31525         return null;
31526     },
31527
31528     onKeyDown : function(e){
31529         var s = this.selNode || this.lastSelNode;
31530         // undesirable, but required
31531         var sm = this;
31532         if(!s){
31533             return;
31534         }
31535         var k = e.getKey();
31536         switch(k){
31537              case e.DOWN:
31538                  e.stopEvent();
31539                  this.selectNext();
31540              break;
31541              case e.UP:
31542                  e.stopEvent();
31543                  this.selectPrevious();
31544              break;
31545              case e.RIGHT:
31546                  e.preventDefault();
31547                  if(s.hasChildNodes()){
31548                      if(!s.isExpanded()){
31549                          s.expand();
31550                      }else if(s.firstChild){
31551                          this.select(s.firstChild, e);
31552                      }
31553                  }
31554              break;
31555              case e.LEFT:
31556                  e.preventDefault();
31557                  if(s.hasChildNodes() && s.isExpanded()){
31558                      s.collapse();
31559                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31560                      this.select(s.parentNode, e);
31561                  }
31562              break;
31563         };
31564     }
31565 });
31566
31567 /**
31568  * @class Roo.tree.MultiSelectionModel
31569  * @extends Roo.util.Observable
31570  * Multi selection for a TreePanel.
31571  * @param {Object} cfg Configuration
31572  */
31573 Roo.tree.MultiSelectionModel = function(){
31574    this.selNodes = [];
31575    this.selMap = {};
31576    this.addEvents({
31577        /**
31578         * @event selectionchange
31579         * Fires when the selected nodes change
31580         * @param {MultiSelectionModel} this
31581         * @param {Array} nodes Array of the selected nodes
31582         */
31583        "selectionchange" : true
31584    });
31585    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31586    
31587 };
31588
31589 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31590     init : function(tree){
31591         this.tree = tree;
31592         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31593         tree.on("click", this.onNodeClick, this);
31594     },
31595     
31596     onNodeClick : function(node, e){
31597         this.select(node, e, e.ctrlKey);
31598     },
31599     
31600     /**
31601      * Select a node.
31602      * @param {TreeNode} node The node to select
31603      * @param {EventObject} e (optional) An event associated with the selection
31604      * @param {Boolean} keepExisting True to retain existing selections
31605      * @return {TreeNode} The selected node
31606      */
31607     select : function(node, e, keepExisting){
31608         if(keepExisting !== true){
31609             this.clearSelections(true);
31610         }
31611         if(this.isSelected(node)){
31612             this.lastSelNode = node;
31613             return node;
31614         }
31615         this.selNodes.push(node);
31616         this.selMap[node.id] = node;
31617         this.lastSelNode = node;
31618         node.ui.onSelectedChange(true);
31619         this.fireEvent("selectionchange", this, this.selNodes);
31620         return node;
31621     },
31622     
31623     /**
31624      * Deselect a node.
31625      * @param {TreeNode} node The node to unselect
31626      */
31627     unselect : function(node){
31628         if(this.selMap[node.id]){
31629             node.ui.onSelectedChange(false);
31630             var sn = this.selNodes;
31631             var index = -1;
31632             if(sn.indexOf){
31633                 index = sn.indexOf(node);
31634             }else{
31635                 for(var i = 0, len = sn.length; i < len; i++){
31636                     if(sn[i] == node){
31637                         index = i;
31638                         break;
31639                     }
31640                 }
31641             }
31642             if(index != -1){
31643                 this.selNodes.splice(index, 1);
31644             }
31645             delete this.selMap[node.id];
31646             this.fireEvent("selectionchange", this, this.selNodes);
31647         }
31648     },
31649     
31650     /**
31651      * Clear all selections
31652      */
31653     clearSelections : function(suppressEvent){
31654         var sn = this.selNodes;
31655         if(sn.length > 0){
31656             for(var i = 0, len = sn.length; i < len; i++){
31657                 sn[i].ui.onSelectedChange(false);
31658             }
31659             this.selNodes = [];
31660             this.selMap = {};
31661             if(suppressEvent !== true){
31662                 this.fireEvent("selectionchange", this, this.selNodes);
31663             }
31664         }
31665     },
31666     
31667     /**
31668      * Returns true if the node is selected
31669      * @param {TreeNode} node The node to check
31670      * @return {Boolean}
31671      */
31672     isSelected : function(node){
31673         return this.selMap[node.id] ? true : false;  
31674     },
31675     
31676     /**
31677      * Returns an array of the selected nodes
31678      * @return {Array}
31679      */
31680     getSelectedNodes : function(){
31681         return this.selNodes;    
31682     },
31683
31684     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31685
31686     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31687
31688     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31689 });/*
31690  * Based on:
31691  * Ext JS Library 1.1.1
31692  * Copyright(c) 2006-2007, Ext JS, LLC.
31693  *
31694  * Originally Released Under LGPL - original licence link has changed is not relivant.
31695  *
31696  * Fork - LGPL
31697  * <script type="text/javascript">
31698  */
31699  
31700 /**
31701  * @class Roo.tree.TreeNode
31702  * @extends Roo.data.Node
31703  * @cfg {String} text The text for this node
31704  * @cfg {Boolean} expanded true to start the node expanded
31705  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31706  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31707  * @cfg {Boolean} disabled true to start the node disabled
31708  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31709  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31710  * @cfg {String} cls A css class to be added to the node
31711  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31712  * @cfg {String} href URL of the link used for the node (defaults to #)
31713  * @cfg {String} hrefTarget target frame for the link
31714  * @cfg {String} qtip An Ext QuickTip for the node
31715  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31716  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31717  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31718  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31719  * (defaults to undefined with no checkbox rendered)
31720  * @constructor
31721  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31722  */
31723 Roo.tree.TreeNode = function(attributes){
31724     attributes = attributes || {};
31725     if(typeof attributes == "string"){
31726         attributes = {text: attributes};
31727     }
31728     this.childrenRendered = false;
31729     this.rendered = false;
31730     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31731     this.expanded = attributes.expanded === true;
31732     this.isTarget = attributes.isTarget !== false;
31733     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31734     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31735
31736     /**
31737      * Read-only. The text for this node. To change it use setText().
31738      * @type String
31739      */
31740     this.text = attributes.text;
31741     /**
31742      * True if this node is disabled.
31743      * @type Boolean
31744      */
31745     this.disabled = attributes.disabled === true;
31746
31747     this.addEvents({
31748         /**
31749         * @event textchange
31750         * Fires when the text for this node is changed
31751         * @param {Node} this This node
31752         * @param {String} text The new text
31753         * @param {String} oldText The old text
31754         */
31755         "textchange" : true,
31756         /**
31757         * @event beforeexpand
31758         * Fires before this node is expanded, return false to cancel.
31759         * @param {Node} this This node
31760         * @param {Boolean} deep
31761         * @param {Boolean} anim
31762         */
31763         "beforeexpand" : true,
31764         /**
31765         * @event beforecollapse
31766         * Fires before this node is collapsed, return false to cancel.
31767         * @param {Node} this This node
31768         * @param {Boolean} deep
31769         * @param {Boolean} anim
31770         */
31771         "beforecollapse" : true,
31772         /**
31773         * @event expand
31774         * Fires when this node is expanded
31775         * @param {Node} this This node
31776         */
31777         "expand" : true,
31778         /**
31779         * @event disabledchange
31780         * Fires when the disabled status of this node changes
31781         * @param {Node} this This node
31782         * @param {Boolean} disabled
31783         */
31784         "disabledchange" : true,
31785         /**
31786         * @event collapse
31787         * Fires when this node is collapsed
31788         * @param {Node} this This node
31789         */
31790         "collapse" : true,
31791         /**
31792         * @event beforeclick
31793         * Fires before click processing. Return false to cancel the default action.
31794         * @param {Node} this This node
31795         * @param {Roo.EventObject} e The event object
31796         */
31797         "beforeclick":true,
31798         /**
31799         * @event checkchange
31800         * Fires when a node with a checkbox's checked property changes
31801         * @param {Node} this This node
31802         * @param {Boolean} checked
31803         */
31804         "checkchange":true,
31805         /**
31806         * @event click
31807         * Fires when this node is clicked
31808         * @param {Node} this This node
31809         * @param {Roo.EventObject} e The event object
31810         */
31811         "click":true,
31812         /**
31813         * @event dblclick
31814         * Fires when this node is double clicked
31815         * @param {Node} this This node
31816         * @param {Roo.EventObject} e The event object
31817         */
31818         "dblclick":true,
31819         /**
31820         * @event contextmenu
31821         * Fires when this node is right clicked
31822         * @param {Node} this This node
31823         * @param {Roo.EventObject} e The event object
31824         */
31825         "contextmenu":true,
31826         /**
31827         * @event beforechildrenrendered
31828         * Fires right before the child nodes for this node are rendered
31829         * @param {Node} this This node
31830         */
31831         "beforechildrenrendered":true
31832     });
31833
31834     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31835
31836     /**
31837      * Read-only. The UI for this node
31838      * @type TreeNodeUI
31839      */
31840     this.ui = new uiClass(this);
31841 };
31842 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31843     preventHScroll: true,
31844     /**
31845      * Returns true if this node is expanded
31846      * @return {Boolean}
31847      */
31848     isExpanded : function(){
31849         return this.expanded;
31850     },
31851
31852     /**
31853      * Returns the UI object for this node
31854      * @return {TreeNodeUI}
31855      */
31856     getUI : function(){
31857         return this.ui;
31858     },
31859
31860     // private override
31861     setFirstChild : function(node){
31862         var of = this.firstChild;
31863         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31864         if(this.childrenRendered && of && node != of){
31865             of.renderIndent(true, true);
31866         }
31867         if(this.rendered){
31868             this.renderIndent(true, true);
31869         }
31870     },
31871
31872     // private override
31873     setLastChild : function(node){
31874         var ol = this.lastChild;
31875         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31876         if(this.childrenRendered && ol && node != ol){
31877             ol.renderIndent(true, true);
31878         }
31879         if(this.rendered){
31880             this.renderIndent(true, true);
31881         }
31882     },
31883
31884     // these methods are overridden to provide lazy rendering support
31885     // private override
31886     appendChild : function(){
31887         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31888         if(node && this.childrenRendered){
31889             node.render();
31890         }
31891         this.ui.updateExpandIcon();
31892         return node;
31893     },
31894
31895     // private override
31896     removeChild : function(node){
31897         this.ownerTree.getSelectionModel().unselect(node);
31898         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31899         // if it's been rendered remove dom node
31900         if(this.childrenRendered){
31901             node.ui.remove();
31902         }
31903         if(this.childNodes.length < 1){
31904             this.collapse(false, false);
31905         }else{
31906             this.ui.updateExpandIcon();
31907         }
31908         if(!this.firstChild) {
31909             this.childrenRendered = false;
31910         }
31911         return node;
31912     },
31913
31914     // private override
31915     insertBefore : function(node, refNode){
31916         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31917         if(newNode && refNode && this.childrenRendered){
31918             node.render();
31919         }
31920         this.ui.updateExpandIcon();
31921         return newNode;
31922     },
31923
31924     /**
31925      * Sets the text for this node
31926      * @param {String} text
31927      */
31928     setText : function(text){
31929         var oldText = this.text;
31930         this.text = text;
31931         this.attributes.text = text;
31932         if(this.rendered){ // event without subscribing
31933             this.ui.onTextChange(this, text, oldText);
31934         }
31935         this.fireEvent("textchange", this, text, oldText);
31936     },
31937
31938     /**
31939      * Triggers selection of this node
31940      */
31941     select : function(){
31942         this.getOwnerTree().getSelectionModel().select(this);
31943     },
31944
31945     /**
31946      * Triggers deselection of this node
31947      */
31948     unselect : function(){
31949         this.getOwnerTree().getSelectionModel().unselect(this);
31950     },
31951
31952     /**
31953      * Returns true if this node is selected
31954      * @return {Boolean}
31955      */
31956     isSelected : function(){
31957         return this.getOwnerTree().getSelectionModel().isSelected(this);
31958     },
31959
31960     /**
31961      * Expand this node.
31962      * @param {Boolean} deep (optional) True to expand all children as well
31963      * @param {Boolean} anim (optional) false to cancel the default animation
31964      * @param {Function} callback (optional) A callback to be called when
31965      * expanding this node completes (does not wait for deep expand to complete).
31966      * Called with 1 parameter, this node.
31967      */
31968     expand : function(deep, anim, callback){
31969         if(!this.expanded){
31970             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31971                 return;
31972             }
31973             if(!this.childrenRendered){
31974                 this.renderChildren();
31975             }
31976             this.expanded = true;
31977             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31978                 this.ui.animExpand(function(){
31979                     this.fireEvent("expand", this);
31980                     if(typeof callback == "function"){
31981                         callback(this);
31982                     }
31983                     if(deep === true){
31984                         this.expandChildNodes(true);
31985                     }
31986                 }.createDelegate(this));
31987                 return;
31988             }else{
31989                 this.ui.expand();
31990                 this.fireEvent("expand", this);
31991                 if(typeof callback == "function"){
31992                     callback(this);
31993                 }
31994             }
31995         }else{
31996            if(typeof callback == "function"){
31997                callback(this);
31998            }
31999         }
32000         if(deep === true){
32001             this.expandChildNodes(true);
32002         }
32003     },
32004
32005     isHiddenRoot : function(){
32006         return this.isRoot && !this.getOwnerTree().rootVisible;
32007     },
32008
32009     /**
32010      * Collapse this node.
32011      * @param {Boolean} deep (optional) True to collapse all children as well
32012      * @param {Boolean} anim (optional) false to cancel the default animation
32013      */
32014     collapse : function(deep, anim){
32015         if(this.expanded && !this.isHiddenRoot()){
32016             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32017                 return;
32018             }
32019             this.expanded = false;
32020             if((this.getOwnerTree().animate && anim !== false) || anim){
32021                 this.ui.animCollapse(function(){
32022                     this.fireEvent("collapse", this);
32023                     if(deep === true){
32024                         this.collapseChildNodes(true);
32025                     }
32026                 }.createDelegate(this));
32027                 return;
32028             }else{
32029                 this.ui.collapse();
32030                 this.fireEvent("collapse", this);
32031             }
32032         }
32033         if(deep === true){
32034             var cs = this.childNodes;
32035             for(var i = 0, len = cs.length; i < len; i++) {
32036                 cs[i].collapse(true, false);
32037             }
32038         }
32039     },
32040
32041     // private
32042     delayedExpand : function(delay){
32043         if(!this.expandProcId){
32044             this.expandProcId = this.expand.defer(delay, this);
32045         }
32046     },
32047
32048     // private
32049     cancelExpand : function(){
32050         if(this.expandProcId){
32051             clearTimeout(this.expandProcId);
32052         }
32053         this.expandProcId = false;
32054     },
32055
32056     /**
32057      * Toggles expanded/collapsed state of the node
32058      */
32059     toggle : function(){
32060         if(this.expanded){
32061             this.collapse();
32062         }else{
32063             this.expand();
32064         }
32065     },
32066
32067     /**
32068      * Ensures all parent nodes are expanded
32069      */
32070     ensureVisible : function(callback){
32071         var tree = this.getOwnerTree();
32072         tree.expandPath(this.parentNode.getPath(), false, function(){
32073             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32074             Roo.callback(callback);
32075         }.createDelegate(this));
32076     },
32077
32078     /**
32079      * Expand all child nodes
32080      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32081      */
32082     expandChildNodes : function(deep){
32083         var cs = this.childNodes;
32084         for(var i = 0, len = cs.length; i < len; i++) {
32085                 cs[i].expand(deep);
32086         }
32087     },
32088
32089     /**
32090      * Collapse all child nodes
32091      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32092      */
32093     collapseChildNodes : function(deep){
32094         var cs = this.childNodes;
32095         for(var i = 0, len = cs.length; i < len; i++) {
32096                 cs[i].collapse(deep);
32097         }
32098     },
32099
32100     /**
32101      * Disables this node
32102      */
32103     disable : function(){
32104         this.disabled = true;
32105         this.unselect();
32106         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32107             this.ui.onDisableChange(this, true);
32108         }
32109         this.fireEvent("disabledchange", this, true);
32110     },
32111
32112     /**
32113      * Enables this node
32114      */
32115     enable : function(){
32116         this.disabled = false;
32117         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32118             this.ui.onDisableChange(this, false);
32119         }
32120         this.fireEvent("disabledchange", this, false);
32121     },
32122
32123     // private
32124     renderChildren : function(suppressEvent){
32125         if(suppressEvent !== false){
32126             this.fireEvent("beforechildrenrendered", this);
32127         }
32128         var cs = this.childNodes;
32129         for(var i = 0, len = cs.length; i < len; i++){
32130             cs[i].render(true);
32131         }
32132         this.childrenRendered = true;
32133     },
32134
32135     // private
32136     sort : function(fn, scope){
32137         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32138         if(this.childrenRendered){
32139             var cs = this.childNodes;
32140             for(var i = 0, len = cs.length; i < len; i++){
32141                 cs[i].render(true);
32142             }
32143         }
32144     },
32145
32146     // private
32147     render : function(bulkRender){
32148         this.ui.render(bulkRender);
32149         if(!this.rendered){
32150             this.rendered = true;
32151             if(this.expanded){
32152                 this.expanded = false;
32153                 this.expand(false, false);
32154             }
32155         }
32156     },
32157
32158     // private
32159     renderIndent : function(deep, refresh){
32160         if(refresh){
32161             this.ui.childIndent = null;
32162         }
32163         this.ui.renderIndent();
32164         if(deep === true && this.childrenRendered){
32165             var cs = this.childNodes;
32166             for(var i = 0, len = cs.length; i < len; i++){
32167                 cs[i].renderIndent(true, refresh);
32168             }
32169         }
32170     }
32171 });/*
32172  * Based on:
32173  * Ext JS Library 1.1.1
32174  * Copyright(c) 2006-2007, Ext JS, LLC.
32175  *
32176  * Originally Released Under LGPL - original licence link has changed is not relivant.
32177  *
32178  * Fork - LGPL
32179  * <script type="text/javascript">
32180  */
32181  
32182 /**
32183  * @class Roo.tree.AsyncTreeNode
32184  * @extends Roo.tree.TreeNode
32185  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32186  * @constructor
32187  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32188  */
32189  Roo.tree.AsyncTreeNode = function(config){
32190     this.loaded = false;
32191     this.loading = false;
32192     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32193     /**
32194     * @event beforeload
32195     * Fires before this node is loaded, return false to cancel
32196     * @param {Node} this This node
32197     */
32198     this.addEvents({'beforeload':true, 'load': true});
32199     /**
32200     * @event load
32201     * Fires when this node is loaded
32202     * @param {Node} this This node
32203     */
32204     /**
32205      * The loader used by this node (defaults to using the tree's defined loader)
32206      * @type TreeLoader
32207      * @property loader
32208      */
32209 };
32210 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32211     expand : function(deep, anim, callback){
32212         if(this.loading){ // if an async load is already running, waiting til it's done
32213             var timer;
32214             var f = function(){
32215                 if(!this.loading){ // done loading
32216                     clearInterval(timer);
32217                     this.expand(deep, anim, callback);
32218                 }
32219             }.createDelegate(this);
32220             timer = setInterval(f, 200);
32221             return;
32222         }
32223         if(!this.loaded){
32224             if(this.fireEvent("beforeload", this) === false){
32225                 return;
32226             }
32227             this.loading = true;
32228             this.ui.beforeLoad(this);
32229             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32230             if(loader){
32231                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32232                 return;
32233             }
32234         }
32235         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32236     },
32237     
32238     /**
32239      * Returns true if this node is currently loading
32240      * @return {Boolean}
32241      */
32242     isLoading : function(){
32243         return this.loading;  
32244     },
32245     
32246     loadComplete : function(deep, anim, callback){
32247         this.loading = false;
32248         this.loaded = true;
32249         this.ui.afterLoad(this);
32250         this.fireEvent("load", this);
32251         this.expand(deep, anim, callback);
32252     },
32253     
32254     /**
32255      * Returns true if this node has been loaded
32256      * @return {Boolean}
32257      */
32258     isLoaded : function(){
32259         return this.loaded;
32260     },
32261     
32262     hasChildNodes : function(){
32263         if(!this.isLeaf() && !this.loaded){
32264             return true;
32265         }else{
32266             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32267         }
32268     },
32269
32270     /**
32271      * Trigger a reload for this node
32272      * @param {Function} callback
32273      */
32274     reload : function(callback){
32275         this.collapse(false, false);
32276         while(this.firstChild){
32277             this.removeChild(this.firstChild);
32278         }
32279         this.childrenRendered = false;
32280         this.loaded = false;
32281         if(this.isHiddenRoot()){
32282             this.expanded = false;
32283         }
32284         this.expand(false, false, callback);
32285     }
32286 });/*
32287  * Based on:
32288  * Ext JS Library 1.1.1
32289  * Copyright(c) 2006-2007, Ext JS, LLC.
32290  *
32291  * Originally Released Under LGPL - original licence link has changed is not relivant.
32292  *
32293  * Fork - LGPL
32294  * <script type="text/javascript">
32295  */
32296  
32297 /**
32298  * @class Roo.tree.TreeNodeUI
32299  * @constructor
32300  * @param {Object} node The node to render
32301  * The TreeNode UI implementation is separate from the
32302  * tree implementation. Unless you are customizing the tree UI,
32303  * you should never have to use this directly.
32304  */
32305 Roo.tree.TreeNodeUI = function(node){
32306     this.node = node;
32307     this.rendered = false;
32308     this.animating = false;
32309     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32310 };
32311
32312 Roo.tree.TreeNodeUI.prototype = {
32313     removeChild : function(node){
32314         if(this.rendered){
32315             this.ctNode.removeChild(node.ui.getEl());
32316         }
32317     },
32318
32319     beforeLoad : function(){
32320          this.addClass("x-tree-node-loading");
32321     },
32322
32323     afterLoad : function(){
32324          this.removeClass("x-tree-node-loading");
32325     },
32326
32327     onTextChange : function(node, text, oldText){
32328         if(this.rendered){
32329             this.textNode.innerHTML = text;
32330         }
32331     },
32332
32333     onDisableChange : function(node, state){
32334         this.disabled = state;
32335         if(state){
32336             this.addClass("x-tree-node-disabled");
32337         }else{
32338             this.removeClass("x-tree-node-disabled");
32339         }
32340     },
32341
32342     onSelectedChange : function(state){
32343         if(state){
32344             this.focus();
32345             this.addClass("x-tree-selected");
32346         }else{
32347             //this.blur();
32348             this.removeClass("x-tree-selected");
32349         }
32350     },
32351
32352     onMove : function(tree, node, oldParent, newParent, index, refNode){
32353         this.childIndent = null;
32354         if(this.rendered){
32355             var targetNode = newParent.ui.getContainer();
32356             if(!targetNode){//target not rendered
32357                 this.holder = document.createElement("div");
32358                 this.holder.appendChild(this.wrap);
32359                 return;
32360             }
32361             var insertBefore = refNode ? refNode.ui.getEl() : null;
32362             if(insertBefore){
32363                 targetNode.insertBefore(this.wrap, insertBefore);
32364             }else{
32365                 targetNode.appendChild(this.wrap);
32366             }
32367             this.node.renderIndent(true);
32368         }
32369     },
32370
32371     addClass : function(cls){
32372         if(this.elNode){
32373             Roo.fly(this.elNode).addClass(cls);
32374         }
32375     },
32376
32377     removeClass : function(cls){
32378         if(this.elNode){
32379             Roo.fly(this.elNode).removeClass(cls);
32380         }
32381     },
32382
32383     remove : function(){
32384         if(this.rendered){
32385             this.holder = document.createElement("div");
32386             this.holder.appendChild(this.wrap);
32387         }
32388     },
32389
32390     fireEvent : function(){
32391         return this.node.fireEvent.apply(this.node, arguments);
32392     },
32393
32394     initEvents : function(){
32395         this.node.on("move", this.onMove, this);
32396         var E = Roo.EventManager;
32397         var a = this.anchor;
32398
32399         var el = Roo.fly(a, '_treeui');
32400
32401         if(Roo.isOpera){ // opera render bug ignores the CSS
32402             el.setStyle("text-decoration", "none");
32403         }
32404
32405         el.on("click", this.onClick, this);
32406         el.on("dblclick", this.onDblClick, this);
32407
32408         if(this.checkbox){
32409             Roo.EventManager.on(this.checkbox,
32410                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32411         }
32412
32413         el.on("contextmenu", this.onContextMenu, this);
32414
32415         var icon = Roo.fly(this.iconNode);
32416         icon.on("click", this.onClick, this);
32417         icon.on("dblclick", this.onDblClick, this);
32418         icon.on("contextmenu", this.onContextMenu, this);
32419         E.on(this.ecNode, "click", this.ecClick, this, true);
32420
32421         if(this.node.disabled){
32422             this.addClass("x-tree-node-disabled");
32423         }
32424         if(this.node.hidden){
32425             this.addClass("x-tree-node-disabled");
32426         }
32427         var ot = this.node.getOwnerTree();
32428         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32429         if(dd && (!this.node.isRoot || ot.rootVisible)){
32430             Roo.dd.Registry.register(this.elNode, {
32431                 node: this.node,
32432                 handles: this.getDDHandles(),
32433                 isHandle: false
32434             });
32435         }
32436     },
32437
32438     getDDHandles : function(){
32439         return [this.iconNode, this.textNode];
32440     },
32441
32442     hide : function(){
32443         if(this.rendered){
32444             this.wrap.style.display = "none";
32445         }
32446     },
32447
32448     show : function(){
32449         if(this.rendered){
32450             this.wrap.style.display = "";
32451         }
32452     },
32453
32454     onContextMenu : function(e){
32455         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32456             e.preventDefault();
32457             this.focus();
32458             this.fireEvent("contextmenu", this.node, e);
32459         }
32460     },
32461
32462     onClick : function(e){
32463         if(this.dropping){
32464             e.stopEvent();
32465             return;
32466         }
32467         if(this.fireEvent("beforeclick", this.node, e) !== false){
32468             if(!this.disabled && this.node.attributes.href){
32469                 this.fireEvent("click", this.node, e);
32470                 return;
32471             }
32472             e.preventDefault();
32473             if(this.disabled){
32474                 return;
32475             }
32476
32477             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32478                 this.node.toggle();
32479             }
32480
32481             this.fireEvent("click", this.node, e);
32482         }else{
32483             e.stopEvent();
32484         }
32485     },
32486
32487     onDblClick : function(e){
32488         e.preventDefault();
32489         if(this.disabled){
32490             return;
32491         }
32492         if(this.checkbox){
32493             this.toggleCheck();
32494         }
32495         if(!this.animating && this.node.hasChildNodes()){
32496             this.node.toggle();
32497         }
32498         this.fireEvent("dblclick", this.node, e);
32499     },
32500
32501     onCheckChange : function(){
32502         var checked = this.checkbox.checked;
32503         this.node.attributes.checked = checked;
32504         this.fireEvent('checkchange', this.node, checked);
32505     },
32506
32507     ecClick : function(e){
32508         if(!this.animating && this.node.hasChildNodes()){
32509             this.node.toggle();
32510         }
32511     },
32512
32513     startDrop : function(){
32514         this.dropping = true;
32515     },
32516
32517     // delayed drop so the click event doesn't get fired on a drop
32518     endDrop : function(){
32519        setTimeout(function(){
32520            this.dropping = false;
32521        }.createDelegate(this), 50);
32522     },
32523
32524     expand : function(){
32525         this.updateExpandIcon();
32526         this.ctNode.style.display = "";
32527     },
32528
32529     focus : function(){
32530         if(!this.node.preventHScroll){
32531             try{this.anchor.focus();
32532             }catch(e){}
32533         }else if(!Roo.isIE){
32534             try{
32535                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32536                 var l = noscroll.scrollLeft;
32537                 this.anchor.focus();
32538                 noscroll.scrollLeft = l;
32539             }catch(e){}
32540         }
32541     },
32542
32543     toggleCheck : function(value){
32544         var cb = this.checkbox;
32545         if(cb){
32546             cb.checked = (value === undefined ? !cb.checked : value);
32547         }
32548     },
32549
32550     blur : function(){
32551         try{
32552             this.anchor.blur();
32553         }catch(e){}
32554     },
32555
32556     animExpand : function(callback){
32557         var ct = Roo.get(this.ctNode);
32558         ct.stopFx();
32559         if(!this.node.hasChildNodes()){
32560             this.updateExpandIcon();
32561             this.ctNode.style.display = "";
32562             Roo.callback(callback);
32563             return;
32564         }
32565         this.animating = true;
32566         this.updateExpandIcon();
32567
32568         ct.slideIn('t', {
32569            callback : function(){
32570                this.animating = false;
32571                Roo.callback(callback);
32572             },
32573             scope: this,
32574             duration: this.node.ownerTree.duration || .25
32575         });
32576     },
32577
32578     highlight : function(){
32579         var tree = this.node.getOwnerTree();
32580         Roo.fly(this.wrap).highlight(
32581             tree.hlColor || "C3DAF9",
32582             {endColor: tree.hlBaseColor}
32583         );
32584     },
32585
32586     collapse : function(){
32587         this.updateExpandIcon();
32588         this.ctNode.style.display = "none";
32589     },
32590
32591     animCollapse : function(callback){
32592         var ct = Roo.get(this.ctNode);
32593         ct.enableDisplayMode('block');
32594         ct.stopFx();
32595
32596         this.animating = true;
32597         this.updateExpandIcon();
32598
32599         ct.slideOut('t', {
32600             callback : function(){
32601                this.animating = false;
32602                Roo.callback(callback);
32603             },
32604             scope: this,
32605             duration: this.node.ownerTree.duration || .25
32606         });
32607     },
32608
32609     getContainer : function(){
32610         return this.ctNode;
32611     },
32612
32613     getEl : function(){
32614         return this.wrap;
32615     },
32616
32617     appendDDGhost : function(ghostNode){
32618         ghostNode.appendChild(this.elNode.cloneNode(true));
32619     },
32620
32621     getDDRepairXY : function(){
32622         return Roo.lib.Dom.getXY(this.iconNode);
32623     },
32624
32625     onRender : function(){
32626         this.render();
32627     },
32628
32629     render : function(bulkRender){
32630         var n = this.node, a = n.attributes;
32631         var targetNode = n.parentNode ?
32632               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32633
32634         if(!this.rendered){
32635             this.rendered = true;
32636
32637             this.renderElements(n, a, targetNode, bulkRender);
32638
32639             if(a.qtip){
32640                if(this.textNode.setAttributeNS){
32641                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32642                    if(a.qtipTitle){
32643                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32644                    }
32645                }else{
32646                    this.textNode.setAttribute("ext:qtip", a.qtip);
32647                    if(a.qtipTitle){
32648                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32649                    }
32650                }
32651             }else if(a.qtipCfg){
32652                 a.qtipCfg.target = Roo.id(this.textNode);
32653                 Roo.QuickTips.register(a.qtipCfg);
32654             }
32655             this.initEvents();
32656             if(!this.node.expanded){
32657                 this.updateExpandIcon();
32658             }
32659         }else{
32660             if(bulkRender === true) {
32661                 targetNode.appendChild(this.wrap);
32662             }
32663         }
32664     },
32665
32666     renderElements : function(n, a, targetNode, bulkRender)
32667     {
32668         // add some indent caching, this helps performance when rendering a large tree
32669         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32670         var t = n.getOwnerTree();
32671         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32672         if (typeof(n.attributes.html) != 'undefined') {
32673             txt = n.attributes.html;
32674         }
32675         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32676         var cb = typeof a.checked == 'boolean';
32677         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32678         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32679             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32680             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32681             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32682             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32683             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32684              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32685                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32686             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32687             "</li>"];
32688
32689         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32690             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32691                                 n.nextSibling.ui.getEl(), buf.join(""));
32692         }else{
32693             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32694         }
32695
32696         this.elNode = this.wrap.childNodes[0];
32697         this.ctNode = this.wrap.childNodes[1];
32698         var cs = this.elNode.childNodes;
32699         this.indentNode = cs[0];
32700         this.ecNode = cs[1];
32701         this.iconNode = cs[2];
32702         var index = 3;
32703         if(cb){
32704             this.checkbox = cs[3];
32705             index++;
32706         }
32707         this.anchor = cs[index];
32708         this.textNode = cs[index].firstChild;
32709     },
32710
32711     getAnchor : function(){
32712         return this.anchor;
32713     },
32714
32715     getTextEl : function(){
32716         return this.textNode;
32717     },
32718
32719     getIconEl : function(){
32720         return this.iconNode;
32721     },
32722
32723     isChecked : function(){
32724         return this.checkbox ? this.checkbox.checked : false;
32725     },
32726
32727     updateExpandIcon : function(){
32728         if(this.rendered){
32729             var n = this.node, c1, c2;
32730             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32731             var hasChild = n.hasChildNodes();
32732             if(hasChild){
32733                 if(n.expanded){
32734                     cls += "-minus";
32735                     c1 = "x-tree-node-collapsed";
32736                     c2 = "x-tree-node-expanded";
32737                 }else{
32738                     cls += "-plus";
32739                     c1 = "x-tree-node-expanded";
32740                     c2 = "x-tree-node-collapsed";
32741                 }
32742                 if(this.wasLeaf){
32743                     this.removeClass("x-tree-node-leaf");
32744                     this.wasLeaf = false;
32745                 }
32746                 if(this.c1 != c1 || this.c2 != c2){
32747                     Roo.fly(this.elNode).replaceClass(c1, c2);
32748                     this.c1 = c1; this.c2 = c2;
32749                 }
32750             }else{
32751                 // this changes non-leafs into leafs if they have no children.
32752                 // it's not very rational behaviour..
32753                 
32754                 if(!this.wasLeaf && this.node.leaf){
32755                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32756                     delete this.c1;
32757                     delete this.c2;
32758                     this.wasLeaf = true;
32759                 }
32760             }
32761             var ecc = "x-tree-ec-icon "+cls;
32762             if(this.ecc != ecc){
32763                 this.ecNode.className = ecc;
32764                 this.ecc = ecc;
32765             }
32766         }
32767     },
32768
32769     getChildIndent : function(){
32770         if(!this.childIndent){
32771             var buf = [];
32772             var p = this.node;
32773             while(p){
32774                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32775                     if(!p.isLast()) {
32776                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32777                     } else {
32778                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32779                     }
32780                 }
32781                 p = p.parentNode;
32782             }
32783             this.childIndent = buf.join("");
32784         }
32785         return this.childIndent;
32786     },
32787
32788     renderIndent : function(){
32789         if(this.rendered){
32790             var indent = "";
32791             var p = this.node.parentNode;
32792             if(p){
32793                 indent = p.ui.getChildIndent();
32794             }
32795             if(this.indentMarkup != indent){ // don't rerender if not required
32796                 this.indentNode.innerHTML = indent;
32797                 this.indentMarkup = indent;
32798             }
32799             this.updateExpandIcon();
32800         }
32801     }
32802 };
32803
32804 Roo.tree.RootTreeNodeUI = function(){
32805     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32806 };
32807 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32808     render : function(){
32809         if(!this.rendered){
32810             var targetNode = this.node.ownerTree.innerCt.dom;
32811             this.node.expanded = true;
32812             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32813             this.wrap = this.ctNode = targetNode.firstChild;
32814         }
32815     },
32816     collapse : function(){
32817     },
32818     expand : function(){
32819     }
32820 });/*
32821  * Based on:
32822  * Ext JS Library 1.1.1
32823  * Copyright(c) 2006-2007, Ext JS, LLC.
32824  *
32825  * Originally Released Under LGPL - original licence link has changed is not relivant.
32826  *
32827  * Fork - LGPL
32828  * <script type="text/javascript">
32829  */
32830 /**
32831  * @class Roo.tree.TreeLoader
32832  * @extends Roo.util.Observable
32833  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32834  * nodes from a specified URL. The response must be a javascript Array definition
32835  * who's elements are node definition objects. eg:
32836  * <pre><code>
32837    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32838     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32839 </code></pre>
32840  * <br><br>
32841  * A server request is sent, and child nodes are loaded only when a node is expanded.
32842  * The loading node's id is passed to the server under the parameter name "node" to
32843  * enable the server to produce the correct child nodes.
32844  * <br><br>
32845  * To pass extra parameters, an event handler may be attached to the "beforeload"
32846  * event, and the parameters specified in the TreeLoader's baseParams property:
32847  * <pre><code>
32848     myTreeLoader.on("beforeload", function(treeLoader, node) {
32849         this.baseParams.category = node.attributes.category;
32850     }, this);
32851 </code></pre><
32852  * This would pass an HTTP parameter called "category" to the server containing
32853  * the value of the Node's "category" attribute.
32854  * @constructor
32855  * Creates a new Treeloader.
32856  * @param {Object} config A config object containing config properties.
32857  */
32858 Roo.tree.TreeLoader = function(config){
32859     this.baseParams = {};
32860     this.requestMethod = "POST";
32861     Roo.apply(this, config);
32862
32863     this.addEvents({
32864     
32865         /**
32866          * @event beforeload
32867          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32868          * @param {Object} This TreeLoader object.
32869          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32870          * @param {Object} callback The callback function specified in the {@link #load} call.
32871          */
32872         beforeload : true,
32873         /**
32874          * @event load
32875          * Fires when the node has been successfuly loaded.
32876          * @param {Object} This TreeLoader object.
32877          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32878          * @param {Object} response The response object containing the data from the server.
32879          */
32880         load : true,
32881         /**
32882          * @event loadexception
32883          * Fires if the network request failed.
32884          * @param {Object} This TreeLoader object.
32885          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32886          * @param {Object} response The response object containing the data from the server.
32887          */
32888         loadexception : true,
32889         /**
32890          * @event create
32891          * Fires before a node is created, enabling you to return custom Node types 
32892          * @param {Object} This TreeLoader object.
32893          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32894          */
32895         create : true
32896     });
32897
32898     Roo.tree.TreeLoader.superclass.constructor.call(this);
32899 };
32900
32901 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32902     /**
32903     * @cfg {String} dataUrl The URL from which to request a Json string which
32904     * specifies an array of node definition object representing the child nodes
32905     * to be loaded.
32906     */
32907     /**
32908     * @cfg {Object} baseParams (optional) An object containing properties which
32909     * specify HTTP parameters to be passed to each request for child nodes.
32910     */
32911     /**
32912     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32913     * created by this loader. If the attributes sent by the server have an attribute in this object,
32914     * they take priority.
32915     */
32916     /**
32917     * @cfg {Object} uiProviders (optional) An object containing properties which
32918     * 
32919     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32920     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32921     * <i>uiProvider</i> attribute of a returned child node is a string rather
32922     * than a reference to a TreeNodeUI implementation, this that string value
32923     * is used as a property name in the uiProviders object. You can define the provider named
32924     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32925     */
32926     uiProviders : {},
32927
32928     /**
32929     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32930     * child nodes before loading.
32931     */
32932     clearOnLoad : true,
32933
32934     /**
32935     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32936     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32937     * Grid query { data : [ .....] }
32938     */
32939     
32940     root : false,
32941      /**
32942     * @cfg {String} queryParam (optional) 
32943     * Name of the query as it will be passed on the querystring (defaults to 'node')
32944     * eg. the request will be ?node=[id]
32945     */
32946     
32947     
32948     queryParam: false,
32949     
32950     /**
32951      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32952      * This is called automatically when a node is expanded, but may be used to reload
32953      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32954      * @param {Roo.tree.TreeNode} node
32955      * @param {Function} callback
32956      */
32957     load : function(node, callback){
32958         if(this.clearOnLoad){
32959             while(node.firstChild){
32960                 node.removeChild(node.firstChild);
32961             }
32962         }
32963         if(node.attributes.children){ // preloaded json children
32964             var cs = node.attributes.children;
32965             for(var i = 0, len = cs.length; i < len; i++){
32966                 node.appendChild(this.createNode(cs[i]));
32967             }
32968             if(typeof callback == "function"){
32969                 callback();
32970             }
32971         }else if(this.dataUrl){
32972             this.requestData(node, callback);
32973         }
32974     },
32975
32976     getParams: function(node){
32977         var buf = [], bp = this.baseParams;
32978         for(var key in bp){
32979             if(typeof bp[key] != "function"){
32980                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32981             }
32982         }
32983         var n = this.queryParam === false ? 'node' : this.queryParam;
32984         buf.push(n + "=", encodeURIComponent(node.id));
32985         return buf.join("");
32986     },
32987
32988     requestData : function(node, callback){
32989         if(this.fireEvent("beforeload", this, node, callback) !== false){
32990             this.transId = Roo.Ajax.request({
32991                 method:this.requestMethod,
32992                 url: this.dataUrl||this.url,
32993                 success: this.handleResponse,
32994                 failure: this.handleFailure,
32995                 scope: this,
32996                 argument: {callback: callback, node: node},
32997                 params: this.getParams(node)
32998             });
32999         }else{
33000             // if the load is cancelled, make sure we notify
33001             // the node that we are done
33002             if(typeof callback == "function"){
33003                 callback();
33004             }
33005         }
33006     },
33007
33008     isLoading : function(){
33009         return this.transId ? true : false;
33010     },
33011
33012     abort : function(){
33013         if(this.isLoading()){
33014             Roo.Ajax.abort(this.transId);
33015         }
33016     },
33017
33018     // private
33019     createNode : function(attr)
33020     {
33021         // apply baseAttrs, nice idea Corey!
33022         if(this.baseAttrs){
33023             Roo.applyIf(attr, this.baseAttrs);
33024         }
33025         if(this.applyLoader !== false){
33026             attr.loader = this;
33027         }
33028         // uiProvider = depreciated..
33029         
33030         if(typeof(attr.uiProvider) == 'string'){
33031            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33032                 /**  eval:var:attr */ eval(attr.uiProvider);
33033         }
33034         if(typeof(this.uiProviders['default']) != 'undefined') {
33035             attr.uiProvider = this.uiProviders['default'];
33036         }
33037         
33038         this.fireEvent('create', this, attr);
33039         
33040         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33041         return(attr.leaf ?
33042                         new Roo.tree.TreeNode(attr) :
33043                         new Roo.tree.AsyncTreeNode(attr));
33044     },
33045
33046     processResponse : function(response, node, callback)
33047     {
33048         var json = response.responseText;
33049         try {
33050             
33051             var o = Roo.decode(json);
33052             
33053             if (!o.success) {
33054                 // it's a failure condition.
33055                 var a = response.argument;
33056                 this.fireEvent("loadexception", this, a.node, response);
33057                 Roo.log("Load failed - should have a handler really");
33058                 return;
33059             }
33060             
33061             if (this.root !== false) {
33062                 o = o[this.root];
33063             }
33064             
33065             for(var i = 0, len = o.length; i < len; i++){
33066                 var n = this.createNode(o[i]);
33067                 if(n){
33068                     node.appendChild(n);
33069                 }
33070             }
33071             if(typeof callback == "function"){
33072                 callback(this, node);
33073             }
33074         }catch(e){
33075             this.handleFailure(response);
33076         }
33077     },
33078
33079     handleResponse : function(response){
33080         this.transId = false;
33081         var a = response.argument;
33082         this.processResponse(response, a.node, a.callback);
33083         this.fireEvent("load", this, a.node, response);
33084     },
33085
33086     handleFailure : function(response)
33087     {
33088         // should handle failure better..
33089         this.transId = false;
33090         var a = response.argument;
33091         this.fireEvent("loadexception", this, a.node, response);
33092         if(typeof a.callback == "function"){
33093             a.callback(this, a.node);
33094         }
33095     }
33096 });/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106
33107 /**
33108 * @class Roo.tree.TreeFilter
33109 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33110 * @param {TreePanel} tree
33111 * @param {Object} config (optional)
33112  */
33113 Roo.tree.TreeFilter = function(tree, config){
33114     this.tree = tree;
33115     this.filtered = {};
33116     Roo.apply(this, config);
33117 };
33118
33119 Roo.tree.TreeFilter.prototype = {
33120     clearBlank:false,
33121     reverse:false,
33122     autoClear:false,
33123     remove:false,
33124
33125      /**
33126      * Filter the data by a specific attribute.
33127      * @param {String/RegExp} value Either string that the attribute value
33128      * should start with or a RegExp to test against the attribute
33129      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33130      * @param {TreeNode} startNode (optional) The node to start the filter at.
33131      */
33132     filter : function(value, attr, startNode){
33133         attr = attr || "text";
33134         var f;
33135         if(typeof value == "string"){
33136             var vlen = value.length;
33137             // auto clear empty filter
33138             if(vlen == 0 && this.clearBlank){
33139                 this.clear();
33140                 return;
33141             }
33142             value = value.toLowerCase();
33143             f = function(n){
33144                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33145             };
33146         }else if(value.exec){ // regex?
33147             f = function(n){
33148                 return value.test(n.attributes[attr]);
33149             };
33150         }else{
33151             throw 'Illegal filter type, must be string or regex';
33152         }
33153         this.filterBy(f, null, startNode);
33154         },
33155
33156     /**
33157      * Filter by a function. The passed function will be called with each
33158      * node in the tree (or from the startNode). If the function returns true, the node is kept
33159      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33160      * @param {Function} fn The filter function
33161      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33162      */
33163     filterBy : function(fn, scope, startNode){
33164         startNode = startNode || this.tree.root;
33165         if(this.autoClear){
33166             this.clear();
33167         }
33168         var af = this.filtered, rv = this.reverse;
33169         var f = function(n){
33170             if(n == startNode){
33171                 return true;
33172             }
33173             if(af[n.id]){
33174                 return false;
33175             }
33176             var m = fn.call(scope || n, n);
33177             if(!m || rv){
33178                 af[n.id] = n;
33179                 n.ui.hide();
33180                 return false;
33181             }
33182             return true;
33183         };
33184         startNode.cascade(f);
33185         if(this.remove){
33186            for(var id in af){
33187                if(typeof id != "function"){
33188                    var n = af[id];
33189                    if(n && n.parentNode){
33190                        n.parentNode.removeChild(n);
33191                    }
33192                }
33193            }
33194         }
33195     },
33196
33197     /**
33198      * Clears the current filter. Note: with the "remove" option
33199      * set a filter cannot be cleared.
33200      */
33201     clear : function(){
33202         var t = this.tree;
33203         var af = this.filtered;
33204         for(var id in af){
33205             if(typeof id != "function"){
33206                 var n = af[id];
33207                 if(n){
33208                     n.ui.show();
33209                 }
33210             }
33211         }
33212         this.filtered = {};
33213     }
33214 };
33215 /*
33216  * Based on:
33217  * Ext JS Library 1.1.1
33218  * Copyright(c) 2006-2007, Ext JS, LLC.
33219  *
33220  * Originally Released Under LGPL - original licence link has changed is not relivant.
33221  *
33222  * Fork - LGPL
33223  * <script type="text/javascript">
33224  */
33225  
33226
33227 /**
33228  * @class Roo.tree.TreeSorter
33229  * Provides sorting of nodes in a TreePanel
33230  * 
33231  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33232  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33233  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33234  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33235  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33236  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33237  * @constructor
33238  * @param {TreePanel} tree
33239  * @param {Object} config
33240  */
33241 Roo.tree.TreeSorter = function(tree, config){
33242     Roo.apply(this, config);
33243     tree.on("beforechildrenrendered", this.doSort, this);
33244     tree.on("append", this.updateSort, this);
33245     tree.on("insert", this.updateSort, this);
33246     
33247     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33248     var p = this.property || "text";
33249     var sortType = this.sortType;
33250     var fs = this.folderSort;
33251     var cs = this.caseSensitive === true;
33252     var leafAttr = this.leafAttr || 'leaf';
33253
33254     this.sortFn = function(n1, n2){
33255         if(fs){
33256             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33257                 return 1;
33258             }
33259             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33260                 return -1;
33261             }
33262         }
33263         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33264         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33265         if(v1 < v2){
33266                         return dsc ? +1 : -1;
33267                 }else if(v1 > v2){
33268                         return dsc ? -1 : +1;
33269         }else{
33270                 return 0;
33271         }
33272     };
33273 };
33274
33275 Roo.tree.TreeSorter.prototype = {
33276     doSort : function(node){
33277         node.sort(this.sortFn);
33278     },
33279     
33280     compareNodes : function(n1, n2){
33281         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33282     },
33283     
33284     updateSort : function(tree, node){
33285         if(node.childrenRendered){
33286             this.doSort.defer(1, this, [node]);
33287         }
33288     }
33289 };/*
33290  * Based on:
33291  * Ext JS Library 1.1.1
33292  * Copyright(c) 2006-2007, Ext JS, LLC.
33293  *
33294  * Originally Released Under LGPL - original licence link has changed is not relivant.
33295  *
33296  * Fork - LGPL
33297  * <script type="text/javascript">
33298  */
33299
33300 if(Roo.dd.DropZone){
33301     
33302 Roo.tree.TreeDropZone = function(tree, config){
33303     this.allowParentInsert = false;
33304     this.allowContainerDrop = false;
33305     this.appendOnly = false;
33306     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33307     this.tree = tree;
33308     this.lastInsertClass = "x-tree-no-status";
33309     this.dragOverData = {};
33310 };
33311
33312 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33313     ddGroup : "TreeDD",
33314     
33315     expandDelay : 1000,
33316     
33317     expandNode : function(node){
33318         if(node.hasChildNodes() && !node.isExpanded()){
33319             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33320         }
33321     },
33322     
33323     queueExpand : function(node){
33324         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33325     },
33326     
33327     cancelExpand : function(){
33328         if(this.expandProcId){
33329             clearTimeout(this.expandProcId);
33330             this.expandProcId = false;
33331         }
33332     },
33333     
33334     isValidDropPoint : function(n, pt, dd, e, data){
33335         if(!n || !data){ return false; }
33336         var targetNode = n.node;
33337         var dropNode = data.node;
33338         // default drop rules
33339         if(!(targetNode && targetNode.isTarget && pt)){
33340             return false;
33341         }
33342         if(pt == "append" && targetNode.allowChildren === false){
33343             return false;
33344         }
33345         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33346             return false;
33347         }
33348         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33349             return false;
33350         }
33351         // reuse the object
33352         var overEvent = this.dragOverData;
33353         overEvent.tree = this.tree;
33354         overEvent.target = targetNode;
33355         overEvent.data = data;
33356         overEvent.point = pt;
33357         overEvent.source = dd;
33358         overEvent.rawEvent = e;
33359         overEvent.dropNode = dropNode;
33360         overEvent.cancel = false;  
33361         var result = this.tree.fireEvent("nodedragover", overEvent);
33362         return overEvent.cancel === false && result !== false;
33363     },
33364     
33365     getDropPoint : function(e, n, dd){
33366         var tn = n.node;
33367         if(tn.isRoot){
33368             return tn.allowChildren !== false ? "append" : false; // always append for root
33369         }
33370         var dragEl = n.ddel;
33371         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33372         var y = Roo.lib.Event.getPageY(e);
33373         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33374         
33375         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33376         var noAppend = tn.allowChildren === false;
33377         if(this.appendOnly || tn.parentNode.allowChildren === false){
33378             return noAppend ? false : "append";
33379         }
33380         var noBelow = false;
33381         if(!this.allowParentInsert){
33382             noBelow = tn.hasChildNodes() && tn.isExpanded();
33383         }
33384         var q = (b - t) / (noAppend ? 2 : 3);
33385         if(y >= t && y < (t + q)){
33386             return "above";
33387         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33388             return "below";
33389         }else{
33390             return "append";
33391         }
33392     },
33393     
33394     onNodeEnter : function(n, dd, e, data){
33395         this.cancelExpand();
33396     },
33397     
33398     onNodeOver : function(n, dd, e, data){
33399         var pt = this.getDropPoint(e, n, dd);
33400         var node = n.node;
33401         
33402         // auto node expand check
33403         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33404             this.queueExpand(node);
33405         }else if(pt != "append"){
33406             this.cancelExpand();
33407         }
33408         
33409         // set the insert point style on the target node
33410         var returnCls = this.dropNotAllowed;
33411         if(this.isValidDropPoint(n, pt, dd, e, data)){
33412            if(pt){
33413                var el = n.ddel;
33414                var cls;
33415                if(pt == "above"){
33416                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33417                    cls = "x-tree-drag-insert-above";
33418                }else if(pt == "below"){
33419                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33420                    cls = "x-tree-drag-insert-below";
33421                }else{
33422                    returnCls = "x-tree-drop-ok-append";
33423                    cls = "x-tree-drag-append";
33424                }
33425                if(this.lastInsertClass != cls){
33426                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33427                    this.lastInsertClass = cls;
33428                }
33429            }
33430        }
33431        return returnCls;
33432     },
33433     
33434     onNodeOut : function(n, dd, e, data){
33435         this.cancelExpand();
33436         this.removeDropIndicators(n);
33437     },
33438     
33439     onNodeDrop : function(n, dd, e, data){
33440         var point = this.getDropPoint(e, n, dd);
33441         var targetNode = n.node;
33442         targetNode.ui.startDrop();
33443         if(!this.isValidDropPoint(n, point, dd, e, data)){
33444             targetNode.ui.endDrop();
33445             return false;
33446         }
33447         // first try to find the drop node
33448         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33449         var dropEvent = {
33450             tree : this.tree,
33451             target: targetNode,
33452             data: data,
33453             point: point,
33454             source: dd,
33455             rawEvent: e,
33456             dropNode: dropNode,
33457             cancel: !dropNode   
33458         };
33459         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33460         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33461             targetNode.ui.endDrop();
33462             return false;
33463         }
33464         // allow target changing
33465         targetNode = dropEvent.target;
33466         if(point == "append" && !targetNode.isExpanded()){
33467             targetNode.expand(false, null, function(){
33468                 this.completeDrop(dropEvent);
33469             }.createDelegate(this));
33470         }else{
33471             this.completeDrop(dropEvent);
33472         }
33473         return true;
33474     },
33475     
33476     completeDrop : function(de){
33477         var ns = de.dropNode, p = de.point, t = de.target;
33478         if(!(ns instanceof Array)){
33479             ns = [ns];
33480         }
33481         var n;
33482         for(var i = 0, len = ns.length; i < len; i++){
33483             n = ns[i];
33484             if(p == "above"){
33485                 t.parentNode.insertBefore(n, t);
33486             }else if(p == "below"){
33487                 t.parentNode.insertBefore(n, t.nextSibling);
33488             }else{
33489                 t.appendChild(n);
33490             }
33491         }
33492         n.ui.focus();
33493         if(this.tree.hlDrop){
33494             n.ui.highlight();
33495         }
33496         t.ui.endDrop();
33497         this.tree.fireEvent("nodedrop", de);
33498     },
33499     
33500     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33501         if(this.tree.hlDrop){
33502             dropNode.ui.focus();
33503             dropNode.ui.highlight();
33504         }
33505         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33506     },
33507     
33508     getTree : function(){
33509         return this.tree;
33510     },
33511     
33512     removeDropIndicators : function(n){
33513         if(n && n.ddel){
33514             var el = n.ddel;
33515             Roo.fly(el).removeClass([
33516                     "x-tree-drag-insert-above",
33517                     "x-tree-drag-insert-below",
33518                     "x-tree-drag-append"]);
33519             this.lastInsertClass = "_noclass";
33520         }
33521     },
33522     
33523     beforeDragDrop : function(target, e, id){
33524         this.cancelExpand();
33525         return true;
33526     },
33527     
33528     afterRepair : function(data){
33529         if(data && Roo.enableFx){
33530             data.node.ui.highlight();
33531         }
33532         this.hideProxy();
33533     }    
33534 });
33535
33536 }
33537 /*
33538  * Based on:
33539  * Ext JS Library 1.1.1
33540  * Copyright(c) 2006-2007, Ext JS, LLC.
33541  *
33542  * Originally Released Under LGPL - original licence link has changed is not relivant.
33543  *
33544  * Fork - LGPL
33545  * <script type="text/javascript">
33546  */
33547  
33548
33549 if(Roo.dd.DragZone){
33550 Roo.tree.TreeDragZone = function(tree, config){
33551     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33552     this.tree = tree;
33553 };
33554
33555 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33556     ddGroup : "TreeDD",
33557     
33558     onBeforeDrag : function(data, e){
33559         var n = data.node;
33560         return n && n.draggable && !n.disabled;
33561     },
33562     
33563     onInitDrag : function(e){
33564         var data = this.dragData;
33565         this.tree.getSelectionModel().select(data.node);
33566         this.proxy.update("");
33567         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33568         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33569     },
33570     
33571     getRepairXY : function(e, data){
33572         return data.node.ui.getDDRepairXY();
33573     },
33574     
33575     onEndDrag : function(data, e){
33576         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33577     },
33578     
33579     onValidDrop : function(dd, e, id){
33580         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33581         this.hideProxy();
33582     },
33583     
33584     beforeInvalidDrop : function(e, id){
33585         // this scrolls the original position back into view
33586         var sm = this.tree.getSelectionModel();
33587         sm.clearSelections();
33588         sm.select(this.dragData.node);
33589     }
33590 });
33591 }/*
33592  * Based on:
33593  * Ext JS Library 1.1.1
33594  * Copyright(c) 2006-2007, Ext JS, LLC.
33595  *
33596  * Originally Released Under LGPL - original licence link has changed is not relivant.
33597  *
33598  * Fork - LGPL
33599  * <script type="text/javascript">
33600  */
33601 /**
33602  * @class Roo.tree.TreeEditor
33603  * @extends Roo.Editor
33604  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33605  * as the editor field.
33606  * @constructor
33607  * @param {Object} config (used to be the tree panel.)
33608  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33609  * 
33610  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33611  * @cfg {Roo.form.TextField|Object} field The field configuration
33612  *
33613  * 
33614  */
33615 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33616     var tree = config;
33617     var field;
33618     if (oldconfig) { // old style..
33619         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33620     } else {
33621         // new style..
33622         tree = config.tree;
33623         config.field = config.field  || {};
33624         config.field.xtype = 'TextField';
33625         field = Roo.factory(config.field, Roo.form);
33626     }
33627     config = config || {};
33628     
33629     
33630     this.addEvents({
33631         /**
33632          * @event beforenodeedit
33633          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33634          * false from the handler of this event.
33635          * @param {Editor} this
33636          * @param {Roo.tree.Node} node 
33637          */
33638         "beforenodeedit" : true
33639     });
33640     
33641     //Roo.log(config);
33642     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33643
33644     this.tree = tree;
33645
33646     tree.on('beforeclick', this.beforeNodeClick, this);
33647     tree.getTreeEl().on('mousedown', this.hide, this);
33648     this.on('complete', this.updateNode, this);
33649     this.on('beforestartedit', this.fitToTree, this);
33650     this.on('startedit', this.bindScroll, this, {delay:10});
33651     this.on('specialkey', this.onSpecialKey, this);
33652 };
33653
33654 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33655     /**
33656      * @cfg {String} alignment
33657      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33658      */
33659     alignment: "l-l",
33660     // inherit
33661     autoSize: false,
33662     /**
33663      * @cfg {Boolean} hideEl
33664      * True to hide the bound element while the editor is displayed (defaults to false)
33665      */
33666     hideEl : false,
33667     /**
33668      * @cfg {String} cls
33669      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33670      */
33671     cls: "x-small-editor x-tree-editor",
33672     /**
33673      * @cfg {Boolean} shim
33674      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33675      */
33676     shim:false,
33677     // inherit
33678     shadow:"frame",
33679     /**
33680      * @cfg {Number} maxWidth
33681      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33682      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33683      * scroll and client offsets into account prior to each edit.
33684      */
33685     maxWidth: 250,
33686
33687     editDelay : 350,
33688
33689     // private
33690     fitToTree : function(ed, el){
33691         var td = this.tree.getTreeEl().dom, nd = el.dom;
33692         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33693             td.scrollLeft = nd.offsetLeft;
33694         }
33695         var w = Math.min(
33696                 this.maxWidth,
33697                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33698         this.setSize(w, '');
33699         
33700         return this.fireEvent('beforenodeedit', this, this.editNode);
33701         
33702     },
33703
33704     // private
33705     triggerEdit : function(node){
33706         this.completeEdit();
33707         this.editNode = node;
33708         this.startEdit(node.ui.textNode, node.text);
33709     },
33710
33711     // private
33712     bindScroll : function(){
33713         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33714     },
33715
33716     // private
33717     beforeNodeClick : function(node, e){
33718         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33719         this.lastClick = new Date();
33720         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33721             e.stopEvent();
33722             this.triggerEdit(node);
33723             return false;
33724         }
33725         return true;
33726     },
33727
33728     // private
33729     updateNode : function(ed, value){
33730         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33731         this.editNode.setText(value);
33732     },
33733
33734     // private
33735     onHide : function(){
33736         Roo.tree.TreeEditor.superclass.onHide.call(this);
33737         if(this.editNode){
33738             this.editNode.ui.focus();
33739         }
33740     },
33741
33742     // private
33743     onSpecialKey : function(field, e){
33744         var k = e.getKey();
33745         if(k == e.ESC){
33746             e.stopEvent();
33747             this.cancelEdit();
33748         }else if(k == e.ENTER && !e.hasModifier()){
33749             e.stopEvent();
33750             this.completeEdit();
33751         }
33752     }
33753 });//<Script type="text/javascript">
33754 /*
33755  * Based on:
33756  * Ext JS Library 1.1.1
33757  * Copyright(c) 2006-2007, Ext JS, LLC.
33758  *
33759  * Originally Released Under LGPL - original licence link has changed is not relivant.
33760  *
33761  * Fork - LGPL
33762  * <script type="text/javascript">
33763  */
33764  
33765 /**
33766  * Not documented??? - probably should be...
33767  */
33768
33769 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33770     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33771     
33772     renderElements : function(n, a, targetNode, bulkRender){
33773         //consel.log("renderElements?");
33774         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33775
33776         var t = n.getOwnerTree();
33777         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33778         
33779         var cols = t.columns;
33780         var bw = t.borderWidth;
33781         var c = cols[0];
33782         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33783          var cb = typeof a.checked == "boolean";
33784         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33785         var colcls = 'x-t-' + tid + '-c0';
33786         var buf = [
33787             '<li class="x-tree-node">',
33788             
33789                 
33790                 '<div class="x-tree-node-el ', a.cls,'">',
33791                     // extran...
33792                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33793                 
33794                 
33795                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33796                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33797                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33798                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33799                            (a.iconCls ? ' '+a.iconCls : ''),
33800                            '" unselectable="on" />',
33801                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33802                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33803                              
33804                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33805                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33806                             '<span unselectable="on" qtip="' + tx + '">',
33807                              tx,
33808                              '</span></a>' ,
33809                     '</div>',
33810                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33811                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33812                  ];
33813         for(var i = 1, len = cols.length; i < len; i++){
33814             c = cols[i];
33815             colcls = 'x-t-' + tid + '-c' +i;
33816             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33817             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33818                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33819                       "</div>");
33820          }
33821          
33822          buf.push(
33823             '</a>',
33824             '<div class="x-clear"></div></div>',
33825             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33826             "</li>");
33827         
33828         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33829             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33830                                 n.nextSibling.ui.getEl(), buf.join(""));
33831         }else{
33832             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33833         }
33834         var el = this.wrap.firstChild;
33835         this.elRow = el;
33836         this.elNode = el.firstChild;
33837         this.ranchor = el.childNodes[1];
33838         this.ctNode = this.wrap.childNodes[1];
33839         var cs = el.firstChild.childNodes;
33840         this.indentNode = cs[0];
33841         this.ecNode = cs[1];
33842         this.iconNode = cs[2];
33843         var index = 3;
33844         if(cb){
33845             this.checkbox = cs[3];
33846             index++;
33847         }
33848         this.anchor = cs[index];
33849         
33850         this.textNode = cs[index].firstChild;
33851         
33852         //el.on("click", this.onClick, this);
33853         //el.on("dblclick", this.onDblClick, this);
33854         
33855         
33856        // console.log(this);
33857     },
33858     initEvents : function(){
33859         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33860         
33861             
33862         var a = this.ranchor;
33863
33864         var el = Roo.get(a);
33865
33866         if(Roo.isOpera){ // opera render bug ignores the CSS
33867             el.setStyle("text-decoration", "none");
33868         }
33869
33870         el.on("click", this.onClick, this);
33871         el.on("dblclick", this.onDblClick, this);
33872         el.on("contextmenu", this.onContextMenu, this);
33873         
33874     },
33875     
33876     /*onSelectedChange : function(state){
33877         if(state){
33878             this.focus();
33879             this.addClass("x-tree-selected");
33880         }else{
33881             //this.blur();
33882             this.removeClass("x-tree-selected");
33883         }
33884     },*/
33885     addClass : function(cls){
33886         if(this.elRow){
33887             Roo.fly(this.elRow).addClass(cls);
33888         }
33889         
33890     },
33891     
33892     
33893     removeClass : function(cls){
33894         if(this.elRow){
33895             Roo.fly(this.elRow).removeClass(cls);
33896         }
33897     }
33898
33899     
33900     
33901 });//<Script type="text/javascript">
33902
33903 /*
33904  * Based on:
33905  * Ext JS Library 1.1.1
33906  * Copyright(c) 2006-2007, Ext JS, LLC.
33907  *
33908  * Originally Released Under LGPL - original licence link has changed is not relivant.
33909  *
33910  * Fork - LGPL
33911  * <script type="text/javascript">
33912  */
33913  
33914
33915 /**
33916  * @class Roo.tree.ColumnTree
33917  * @extends Roo.data.TreePanel
33918  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33919  * @cfg {int} borderWidth  compined right/left border allowance
33920  * @constructor
33921  * @param {String/HTMLElement/Element} el The container element
33922  * @param {Object} config
33923  */
33924 Roo.tree.ColumnTree =  function(el, config)
33925 {
33926    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33927    this.addEvents({
33928         /**
33929         * @event resize
33930         * Fire this event on a container when it resizes
33931         * @param {int} w Width
33932         * @param {int} h Height
33933         */
33934        "resize" : true
33935     });
33936     this.on('resize', this.onResize, this);
33937 };
33938
33939 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33940     //lines:false,
33941     
33942     
33943     borderWidth: Roo.isBorderBox ? 0 : 2, 
33944     headEls : false,
33945     
33946     render : function(){
33947         // add the header.....
33948        
33949         Roo.tree.ColumnTree.superclass.render.apply(this);
33950         
33951         this.el.addClass('x-column-tree');
33952         
33953         this.headers = this.el.createChild(
33954             {cls:'x-tree-headers'},this.innerCt.dom);
33955    
33956         var cols = this.columns, c;
33957         var totalWidth = 0;
33958         this.headEls = [];
33959         var  len = cols.length;
33960         for(var i = 0; i < len; i++){
33961              c = cols[i];
33962              totalWidth += c.width;
33963             this.headEls.push(this.headers.createChild({
33964                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33965                  cn: {
33966                      cls:'x-tree-hd-text',
33967                      html: c.header
33968                  },
33969                  style:'width:'+(c.width-this.borderWidth)+'px;'
33970              }));
33971         }
33972         this.headers.createChild({cls:'x-clear'});
33973         // prevent floats from wrapping when clipped
33974         this.headers.setWidth(totalWidth);
33975         //this.innerCt.setWidth(totalWidth);
33976         this.innerCt.setStyle({ overflow: 'auto' });
33977         this.onResize(this.width, this.height);
33978              
33979         
33980     },
33981     onResize : function(w,h)
33982     {
33983         this.height = h;
33984         this.width = w;
33985         // resize cols..
33986         this.innerCt.setWidth(this.width);
33987         this.innerCt.setHeight(this.height-20);
33988         
33989         // headers...
33990         var cols = this.columns, c;
33991         var totalWidth = 0;
33992         var expEl = false;
33993         var len = cols.length;
33994         for(var i = 0; i < len; i++){
33995             c = cols[i];
33996             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33997                 // it's the expander..
33998                 expEl  = this.headEls[i];
33999                 continue;
34000             }
34001             totalWidth += c.width;
34002             
34003         }
34004         if (expEl) {
34005             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34006         }
34007         this.headers.setWidth(w-20);
34008
34009         
34010         
34011         
34012     }
34013 });
34014 /*
34015  * Based on:
34016  * Ext JS Library 1.1.1
34017  * Copyright(c) 2006-2007, Ext JS, LLC.
34018  *
34019  * Originally Released Under LGPL - original licence link has changed is not relivant.
34020  *
34021  * Fork - LGPL
34022  * <script type="text/javascript">
34023  */
34024  
34025 /**
34026  * @class Roo.menu.Menu
34027  * @extends Roo.util.Observable
34028  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34029  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34030  * @constructor
34031  * Creates a new Menu
34032  * @param {Object} config Configuration options
34033  */
34034 Roo.menu.Menu = function(config){
34035     Roo.apply(this, config);
34036     this.id = this.id || Roo.id();
34037     this.addEvents({
34038         /**
34039          * @event beforeshow
34040          * Fires before this menu is displayed
34041          * @param {Roo.menu.Menu} this
34042          */
34043         beforeshow : true,
34044         /**
34045          * @event beforehide
34046          * Fires before this menu is hidden
34047          * @param {Roo.menu.Menu} this
34048          */
34049         beforehide : true,
34050         /**
34051          * @event show
34052          * Fires after this menu is displayed
34053          * @param {Roo.menu.Menu} this
34054          */
34055         show : true,
34056         /**
34057          * @event hide
34058          * Fires after this menu is hidden
34059          * @param {Roo.menu.Menu} this
34060          */
34061         hide : true,
34062         /**
34063          * @event click
34064          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34065          * @param {Roo.menu.Menu} this
34066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34067          * @param {Roo.EventObject} e
34068          */
34069         click : true,
34070         /**
34071          * @event mouseover
34072          * Fires when the mouse is hovering over this menu
34073          * @param {Roo.menu.Menu} this
34074          * @param {Roo.EventObject} e
34075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34076          */
34077         mouseover : true,
34078         /**
34079          * @event mouseout
34080          * Fires when the mouse exits this menu
34081          * @param {Roo.menu.Menu} this
34082          * @param {Roo.EventObject} e
34083          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34084          */
34085         mouseout : true,
34086         /**
34087          * @event itemclick
34088          * Fires when a menu item contained in this menu is clicked
34089          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34090          * @param {Roo.EventObject} e
34091          */
34092         itemclick: true
34093     });
34094     if (this.registerMenu) {
34095         Roo.menu.MenuMgr.register(this);
34096     }
34097     
34098     var mis = this.items;
34099     this.items = new Roo.util.MixedCollection();
34100     if(mis){
34101         this.add.apply(this, mis);
34102     }
34103 };
34104
34105 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34106     /**
34107      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34108      */
34109     minWidth : 120,
34110     /**
34111      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34112      * for bottom-right shadow (defaults to "sides")
34113      */
34114     shadow : "sides",
34115     /**
34116      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34117      * this menu (defaults to "tl-tr?")
34118      */
34119     subMenuAlign : "tl-tr?",
34120     /**
34121      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34122      * relative to its element of origin (defaults to "tl-bl?")
34123      */
34124     defaultAlign : "tl-bl?",
34125     /**
34126      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34127      */
34128     allowOtherMenus : false,
34129     /**
34130      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34131      */
34132     registerMenu : true,
34133
34134     hidden:true,
34135
34136     // private
34137     render : function(){
34138         if(this.el){
34139             return;
34140         }
34141         var el = this.el = new Roo.Layer({
34142             cls: "x-menu",
34143             shadow:this.shadow,
34144             constrain: false,
34145             parentEl: this.parentEl || document.body,
34146             zindex:15000
34147         });
34148
34149         this.keyNav = new Roo.menu.MenuNav(this);
34150
34151         if(this.plain){
34152             el.addClass("x-menu-plain");
34153         }
34154         if(this.cls){
34155             el.addClass(this.cls);
34156         }
34157         // generic focus element
34158         this.focusEl = el.createChild({
34159             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34160         });
34161         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34162         ul.on("click", this.onClick, this);
34163         ul.on("mouseover", this.onMouseOver, this);
34164         ul.on("mouseout", this.onMouseOut, this);
34165         this.items.each(function(item){
34166             var li = document.createElement("li");
34167             li.className = "x-menu-list-item";
34168             ul.dom.appendChild(li);
34169             item.render(li, this);
34170         }, this);
34171         this.ul = ul;
34172         this.autoWidth();
34173     },
34174
34175     // private
34176     autoWidth : function(){
34177         var el = this.el, ul = this.ul;
34178         if(!el){
34179             return;
34180         }
34181         var w = this.width;
34182         if(w){
34183             el.setWidth(w);
34184         }else if(Roo.isIE){
34185             el.setWidth(this.minWidth);
34186             var t = el.dom.offsetWidth; // force recalc
34187             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34188         }
34189     },
34190
34191     // private
34192     delayAutoWidth : function(){
34193         if(this.rendered){
34194             if(!this.awTask){
34195                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34196             }
34197             this.awTask.delay(20);
34198         }
34199     },
34200
34201     // private
34202     findTargetItem : function(e){
34203         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34204         if(t && t.menuItemId){
34205             return this.items.get(t.menuItemId);
34206         }
34207     },
34208
34209     // private
34210     onClick : function(e){
34211         var t;
34212         if(t = this.findTargetItem(e)){
34213             t.onClick(e);
34214             this.fireEvent("click", this, t, e);
34215         }
34216     },
34217
34218     // private
34219     setActiveItem : function(item, autoExpand){
34220         if(item != this.activeItem){
34221             if(this.activeItem){
34222                 this.activeItem.deactivate();
34223             }
34224             this.activeItem = item;
34225             item.activate(autoExpand);
34226         }else if(autoExpand){
34227             item.expandMenu();
34228         }
34229     },
34230
34231     // private
34232     tryActivate : function(start, step){
34233         var items = this.items;
34234         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34235             var item = items.get(i);
34236             if(!item.disabled && item.canActivate){
34237                 this.setActiveItem(item, false);
34238                 return item;
34239             }
34240         }
34241         return false;
34242     },
34243
34244     // private
34245     onMouseOver : function(e){
34246         var t;
34247         if(t = this.findTargetItem(e)){
34248             if(t.canActivate && !t.disabled){
34249                 this.setActiveItem(t, true);
34250             }
34251         }
34252         this.fireEvent("mouseover", this, e, t);
34253     },
34254
34255     // private
34256     onMouseOut : function(e){
34257         var t;
34258         if(t = this.findTargetItem(e)){
34259             if(t == this.activeItem && t.shouldDeactivate(e)){
34260                 this.activeItem.deactivate();
34261                 delete this.activeItem;
34262             }
34263         }
34264         this.fireEvent("mouseout", this, e, t);
34265     },
34266
34267     /**
34268      * Read-only.  Returns true if the menu is currently displayed, else false.
34269      * @type Boolean
34270      */
34271     isVisible : function(){
34272         return this.el && !this.hidden;
34273     },
34274
34275     /**
34276      * Displays this menu relative to another element
34277      * @param {String/HTMLElement/Roo.Element} element The element to align to
34278      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34279      * the element (defaults to this.defaultAlign)
34280      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34281      */
34282     show : function(el, pos, parentMenu){
34283         this.parentMenu = parentMenu;
34284         if(!this.el){
34285             this.render();
34286         }
34287         this.fireEvent("beforeshow", this);
34288         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34289     },
34290
34291     /**
34292      * Displays this menu at a specific xy position
34293      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34294      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34295      */
34296     showAt : function(xy, parentMenu, /* private: */_e){
34297         this.parentMenu = parentMenu;
34298         if(!this.el){
34299             this.render();
34300         }
34301         if(_e !== false){
34302             this.fireEvent("beforeshow", this);
34303             xy = this.el.adjustForConstraints(xy);
34304         }
34305         this.el.setXY(xy);
34306         this.el.show();
34307         this.hidden = false;
34308         this.focus();
34309         this.fireEvent("show", this);
34310     },
34311
34312     focus : function(){
34313         if(!this.hidden){
34314             this.doFocus.defer(50, this);
34315         }
34316     },
34317
34318     doFocus : function(){
34319         if(!this.hidden){
34320             this.focusEl.focus();
34321         }
34322     },
34323
34324     /**
34325      * Hides this menu and optionally all parent menus
34326      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34327      */
34328     hide : function(deep){
34329         if(this.el && this.isVisible()){
34330             this.fireEvent("beforehide", this);
34331             if(this.activeItem){
34332                 this.activeItem.deactivate();
34333                 this.activeItem = null;
34334             }
34335             this.el.hide();
34336             this.hidden = true;
34337             this.fireEvent("hide", this);
34338         }
34339         if(deep === true && this.parentMenu){
34340             this.parentMenu.hide(true);
34341         }
34342     },
34343
34344     /**
34345      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34346      * Any of the following are valid:
34347      * <ul>
34348      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34349      * <li>An HTMLElement object which will be converted to a menu item</li>
34350      * <li>A menu item config object that will be created as a new menu item</li>
34351      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34352      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34353      * </ul>
34354      * Usage:
34355      * <pre><code>
34356 // Create the menu
34357 var menu = new Roo.menu.Menu();
34358
34359 // Create a menu item to add by reference
34360 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34361
34362 // Add a bunch of items at once using different methods.
34363 // Only the last item added will be returned.
34364 var item = menu.add(
34365     menuItem,                // add existing item by ref
34366     'Dynamic Item',          // new TextItem
34367     '-',                     // new separator
34368     { text: 'Config Item' }  // new item by config
34369 );
34370 </code></pre>
34371      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34372      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34373      */
34374     add : function(){
34375         var a = arguments, l = a.length, item;
34376         for(var i = 0; i < l; i++){
34377             var el = a[i];
34378             if ((typeof(el) == "object") && el.xtype && el.xns) {
34379                 el = Roo.factory(el, Roo.menu);
34380             }
34381             
34382             if(el.render){ // some kind of Item
34383                 item = this.addItem(el);
34384             }else if(typeof el == "string"){ // string
34385                 if(el == "separator" || el == "-"){
34386                     item = this.addSeparator();
34387                 }else{
34388                     item = this.addText(el);
34389                 }
34390             }else if(el.tagName || el.el){ // element
34391                 item = this.addElement(el);
34392             }else if(typeof el == "object"){ // must be menu item config?
34393                 item = this.addMenuItem(el);
34394             }
34395         }
34396         return item;
34397     },
34398
34399     /**
34400      * Returns this menu's underlying {@link Roo.Element} object
34401      * @return {Roo.Element} The element
34402      */
34403     getEl : function(){
34404         if(!this.el){
34405             this.render();
34406         }
34407         return this.el;
34408     },
34409
34410     /**
34411      * Adds a separator bar to the menu
34412      * @return {Roo.menu.Item} The menu item that was added
34413      */
34414     addSeparator : function(){
34415         return this.addItem(new Roo.menu.Separator());
34416     },
34417
34418     /**
34419      * Adds an {@link Roo.Element} object to the menu
34420      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34421      * @return {Roo.menu.Item} The menu item that was added
34422      */
34423     addElement : function(el){
34424         return this.addItem(new Roo.menu.BaseItem(el));
34425     },
34426
34427     /**
34428      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34429      * @param {Roo.menu.Item} item The menu item to add
34430      * @return {Roo.menu.Item} The menu item that was added
34431      */
34432     addItem : function(item){
34433         this.items.add(item);
34434         if(this.ul){
34435             var li = document.createElement("li");
34436             li.className = "x-menu-list-item";
34437             this.ul.dom.appendChild(li);
34438             item.render(li, this);
34439             this.delayAutoWidth();
34440         }
34441         return item;
34442     },
34443
34444     /**
34445      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34446      * @param {Object} config A MenuItem config object
34447      * @return {Roo.menu.Item} The menu item that was added
34448      */
34449     addMenuItem : function(config){
34450         if(!(config instanceof Roo.menu.Item)){
34451             if(typeof config.checked == "boolean"){ // must be check menu item config?
34452                 config = new Roo.menu.CheckItem(config);
34453             }else{
34454                 config = new Roo.menu.Item(config);
34455             }
34456         }
34457         return this.addItem(config);
34458     },
34459
34460     /**
34461      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34462      * @param {String} text The text to display in the menu item
34463      * @return {Roo.menu.Item} The menu item that was added
34464      */
34465     addText : function(text){
34466         return this.addItem(new Roo.menu.TextItem({ text : text }));
34467     },
34468
34469     /**
34470      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34471      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34472      * @param {Roo.menu.Item} item The menu item to add
34473      * @return {Roo.menu.Item} The menu item that was added
34474      */
34475     insert : function(index, item){
34476         this.items.insert(index, item);
34477         if(this.ul){
34478             var li = document.createElement("li");
34479             li.className = "x-menu-list-item";
34480             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34481             item.render(li, this);
34482             this.delayAutoWidth();
34483         }
34484         return item;
34485     },
34486
34487     /**
34488      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34489      * @param {Roo.menu.Item} item The menu item to remove
34490      */
34491     remove : function(item){
34492         this.items.removeKey(item.id);
34493         item.destroy();
34494     },
34495
34496     /**
34497      * Removes and destroys all items in the menu
34498      */
34499     removeAll : function(){
34500         var f;
34501         while(f = this.items.first()){
34502             this.remove(f);
34503         }
34504     }
34505 });
34506
34507 // MenuNav is a private utility class used internally by the Menu
34508 Roo.menu.MenuNav = function(menu){
34509     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34510     this.scope = this.menu = menu;
34511 };
34512
34513 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34514     doRelay : function(e, h){
34515         var k = e.getKey();
34516         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34517             this.menu.tryActivate(0, 1);
34518             return false;
34519         }
34520         return h.call(this.scope || this, e, this.menu);
34521     },
34522
34523     up : function(e, m){
34524         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34525             m.tryActivate(m.items.length-1, -1);
34526         }
34527     },
34528
34529     down : function(e, m){
34530         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34531             m.tryActivate(0, 1);
34532         }
34533     },
34534
34535     right : function(e, m){
34536         if(m.activeItem){
34537             m.activeItem.expandMenu(true);
34538         }
34539     },
34540
34541     left : function(e, m){
34542         m.hide();
34543         if(m.parentMenu && m.parentMenu.activeItem){
34544             m.parentMenu.activeItem.activate();
34545         }
34546     },
34547
34548     enter : function(e, m){
34549         if(m.activeItem){
34550             e.stopPropagation();
34551             m.activeItem.onClick(e);
34552             m.fireEvent("click", this, m.activeItem);
34553             return true;
34554         }
34555     }
34556 });/*
34557  * Based on:
34558  * Ext JS Library 1.1.1
34559  * Copyright(c) 2006-2007, Ext JS, LLC.
34560  *
34561  * Originally Released Under LGPL - original licence link has changed is not relivant.
34562  *
34563  * Fork - LGPL
34564  * <script type="text/javascript">
34565  */
34566  
34567 /**
34568  * @class Roo.menu.MenuMgr
34569  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34570  * @singleton
34571  */
34572 Roo.menu.MenuMgr = function(){
34573    var menus, active, groups = {}, attached = false, lastShow = new Date();
34574
34575    // private - called when first menu is created
34576    function init(){
34577        menus = {};
34578        active = new Roo.util.MixedCollection();
34579        Roo.get(document).addKeyListener(27, function(){
34580            if(active.length > 0){
34581                hideAll();
34582            }
34583        });
34584    }
34585
34586    // private
34587    function hideAll(){
34588        if(active && active.length > 0){
34589            var c = active.clone();
34590            c.each(function(m){
34591                m.hide();
34592            });
34593        }
34594    }
34595
34596    // private
34597    function onHide(m){
34598        active.remove(m);
34599        if(active.length < 1){
34600            Roo.get(document).un("mousedown", onMouseDown);
34601            attached = false;
34602        }
34603    }
34604
34605    // private
34606    function onShow(m){
34607        var last = active.last();
34608        lastShow = new Date();
34609        active.add(m);
34610        if(!attached){
34611            Roo.get(document).on("mousedown", onMouseDown);
34612            attached = true;
34613        }
34614        if(m.parentMenu){
34615           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34616           m.parentMenu.activeChild = m;
34617        }else if(last && last.isVisible()){
34618           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34619        }
34620    }
34621
34622    // private
34623    function onBeforeHide(m){
34624        if(m.activeChild){
34625            m.activeChild.hide();
34626        }
34627        if(m.autoHideTimer){
34628            clearTimeout(m.autoHideTimer);
34629            delete m.autoHideTimer;
34630        }
34631    }
34632
34633    // private
34634    function onBeforeShow(m){
34635        var pm = m.parentMenu;
34636        if(!pm && !m.allowOtherMenus){
34637            hideAll();
34638        }else if(pm && pm.activeChild && active != m){
34639            pm.activeChild.hide();
34640        }
34641    }
34642
34643    // private
34644    function onMouseDown(e){
34645        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34646            hideAll();
34647        }
34648    }
34649
34650    // private
34651    function onBeforeCheck(mi, state){
34652        if(state){
34653            var g = groups[mi.group];
34654            for(var i = 0, l = g.length; i < l; i++){
34655                if(g[i] != mi){
34656                    g[i].setChecked(false);
34657                }
34658            }
34659        }
34660    }
34661
34662    return {
34663
34664        /**
34665         * Hides all menus that are currently visible
34666         */
34667        hideAll : function(){
34668             hideAll();  
34669        },
34670
34671        // private
34672        register : function(menu){
34673            if(!menus){
34674                init();
34675            }
34676            menus[menu.id] = menu;
34677            menu.on("beforehide", onBeforeHide);
34678            menu.on("hide", onHide);
34679            menu.on("beforeshow", onBeforeShow);
34680            menu.on("show", onShow);
34681            var g = menu.group;
34682            if(g && menu.events["checkchange"]){
34683                if(!groups[g]){
34684                    groups[g] = [];
34685                }
34686                groups[g].push(menu);
34687                menu.on("checkchange", onCheck);
34688            }
34689        },
34690
34691         /**
34692          * Returns a {@link Roo.menu.Menu} object
34693          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34694          * be used to generate and return a new Menu instance.
34695          */
34696        get : function(menu){
34697            if(typeof menu == "string"){ // menu id
34698                return menus[menu];
34699            }else if(menu.events){  // menu instance
34700                return menu;
34701            }else if(typeof menu.length == 'number'){ // array of menu items?
34702                return new Roo.menu.Menu({items:menu});
34703            }else{ // otherwise, must be a config
34704                return new Roo.menu.Menu(menu);
34705            }
34706        },
34707
34708        // private
34709        unregister : function(menu){
34710            delete menus[menu.id];
34711            menu.un("beforehide", onBeforeHide);
34712            menu.un("hide", onHide);
34713            menu.un("beforeshow", onBeforeShow);
34714            menu.un("show", onShow);
34715            var g = menu.group;
34716            if(g && menu.events["checkchange"]){
34717                groups[g].remove(menu);
34718                menu.un("checkchange", onCheck);
34719            }
34720        },
34721
34722        // private
34723        registerCheckable : function(menuItem){
34724            var g = menuItem.group;
34725            if(g){
34726                if(!groups[g]){
34727                    groups[g] = [];
34728                }
34729                groups[g].push(menuItem);
34730                menuItem.on("beforecheckchange", onBeforeCheck);
34731            }
34732        },
34733
34734        // private
34735        unregisterCheckable : function(menuItem){
34736            var g = menuItem.group;
34737            if(g){
34738                groups[g].remove(menuItem);
34739                menuItem.un("beforecheckchange", onBeforeCheck);
34740            }
34741        }
34742    };
34743 }();/*
34744  * Based on:
34745  * Ext JS Library 1.1.1
34746  * Copyright(c) 2006-2007, Ext JS, LLC.
34747  *
34748  * Originally Released Under LGPL - original licence link has changed is not relivant.
34749  *
34750  * Fork - LGPL
34751  * <script type="text/javascript">
34752  */
34753  
34754
34755 /**
34756  * @class Roo.menu.BaseItem
34757  * @extends Roo.Component
34758  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34759  * management and base configuration options shared by all menu components.
34760  * @constructor
34761  * Creates a new BaseItem
34762  * @param {Object} config Configuration options
34763  */
34764 Roo.menu.BaseItem = function(config){
34765     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34766
34767     this.addEvents({
34768         /**
34769          * @event click
34770          * Fires when this item is clicked
34771          * @param {Roo.menu.BaseItem} this
34772          * @param {Roo.EventObject} e
34773          */
34774         click: true,
34775         /**
34776          * @event activate
34777          * Fires when this item is activated
34778          * @param {Roo.menu.BaseItem} this
34779          */
34780         activate : true,
34781         /**
34782          * @event deactivate
34783          * Fires when this item is deactivated
34784          * @param {Roo.menu.BaseItem} this
34785          */
34786         deactivate : true
34787     });
34788
34789     if(this.handler){
34790         this.on("click", this.handler, this.scope, true);
34791     }
34792 };
34793
34794 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34795     /**
34796      * @cfg {Function} handler
34797      * A function that will handle the click event of this menu item (defaults to undefined)
34798      */
34799     /**
34800      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34801      */
34802     canActivate : false,
34803     /**
34804      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34805      */
34806     activeClass : "x-menu-item-active",
34807     /**
34808      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34809      */
34810     hideOnClick : true,
34811     /**
34812      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34813      */
34814     hideDelay : 100,
34815
34816     // private
34817     ctype: "Roo.menu.BaseItem",
34818
34819     // private
34820     actionMode : "container",
34821
34822     // private
34823     render : function(container, parentMenu){
34824         this.parentMenu = parentMenu;
34825         Roo.menu.BaseItem.superclass.render.call(this, container);
34826         this.container.menuItemId = this.id;
34827     },
34828
34829     // private
34830     onRender : function(container, position){
34831         this.el = Roo.get(this.el);
34832         container.dom.appendChild(this.el.dom);
34833     },
34834
34835     // private
34836     onClick : function(e){
34837         if(!this.disabled && this.fireEvent("click", this, e) !== false
34838                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34839             this.handleClick(e);
34840         }else{
34841             e.stopEvent();
34842         }
34843     },
34844
34845     // private
34846     activate : function(){
34847         if(this.disabled){
34848             return false;
34849         }
34850         var li = this.container;
34851         li.addClass(this.activeClass);
34852         this.region = li.getRegion().adjust(2, 2, -2, -2);
34853         this.fireEvent("activate", this);
34854         return true;
34855     },
34856
34857     // private
34858     deactivate : function(){
34859         this.container.removeClass(this.activeClass);
34860         this.fireEvent("deactivate", this);
34861     },
34862
34863     // private
34864     shouldDeactivate : function(e){
34865         return !this.region || !this.region.contains(e.getPoint());
34866     },
34867
34868     // private
34869     handleClick : function(e){
34870         if(this.hideOnClick){
34871             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34872         }
34873     },
34874
34875     // private
34876     expandMenu : function(autoActivate){
34877         // do nothing
34878     },
34879
34880     // private
34881     hideMenu : function(){
34882         // do nothing
34883     }
34884 });/*
34885  * Based on:
34886  * Ext JS Library 1.1.1
34887  * Copyright(c) 2006-2007, Ext JS, LLC.
34888  *
34889  * Originally Released Under LGPL - original licence link has changed is not relivant.
34890  *
34891  * Fork - LGPL
34892  * <script type="text/javascript">
34893  */
34894  
34895 /**
34896  * @class Roo.menu.Adapter
34897  * @extends Roo.menu.BaseItem
34898  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34899  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34900  * @constructor
34901  * Creates a new Adapter
34902  * @param {Object} config Configuration options
34903  */
34904 Roo.menu.Adapter = function(component, config){
34905     Roo.menu.Adapter.superclass.constructor.call(this, config);
34906     this.component = component;
34907 };
34908 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34909     // private
34910     canActivate : true,
34911
34912     // private
34913     onRender : function(container, position){
34914         this.component.render(container);
34915         this.el = this.component.getEl();
34916     },
34917
34918     // private
34919     activate : function(){
34920         if(this.disabled){
34921             return false;
34922         }
34923         this.component.focus();
34924         this.fireEvent("activate", this);
34925         return true;
34926     },
34927
34928     // private
34929     deactivate : function(){
34930         this.fireEvent("deactivate", this);
34931     },
34932
34933     // private
34934     disable : function(){
34935         this.component.disable();
34936         Roo.menu.Adapter.superclass.disable.call(this);
34937     },
34938
34939     // private
34940     enable : function(){
34941         this.component.enable();
34942         Roo.menu.Adapter.superclass.enable.call(this);
34943     }
34944 });/*
34945  * Based on:
34946  * Ext JS Library 1.1.1
34947  * Copyright(c) 2006-2007, Ext JS, LLC.
34948  *
34949  * Originally Released Under LGPL - original licence link has changed is not relivant.
34950  *
34951  * Fork - LGPL
34952  * <script type="text/javascript">
34953  */
34954
34955 /**
34956  * @class Roo.menu.TextItem
34957  * @extends Roo.menu.BaseItem
34958  * Adds a static text string to a menu, usually used as either a heading or group separator.
34959  * Note: old style constructor with text is still supported.
34960  * 
34961  * @constructor
34962  * Creates a new TextItem
34963  * @param {Object} cfg Configuration
34964  */
34965 Roo.menu.TextItem = function(cfg){
34966     if (typeof(cfg) == 'string') {
34967         this.text = cfg;
34968     } else {
34969         Roo.apply(this,cfg);
34970     }
34971     
34972     Roo.menu.TextItem.superclass.constructor.call(this);
34973 };
34974
34975 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34976     /**
34977      * @cfg {Boolean} text Text to show on item.
34978      */
34979     text : '',
34980     
34981     /**
34982      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34983      */
34984     hideOnClick : false,
34985     /**
34986      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34987      */
34988     itemCls : "x-menu-text",
34989
34990     // private
34991     onRender : function(){
34992         var s = document.createElement("span");
34993         s.className = this.itemCls;
34994         s.innerHTML = this.text;
34995         this.el = s;
34996         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34997     }
34998 });/*
34999  * Based on:
35000  * Ext JS Library 1.1.1
35001  * Copyright(c) 2006-2007, Ext JS, LLC.
35002  *
35003  * Originally Released Under LGPL - original licence link has changed is not relivant.
35004  *
35005  * Fork - LGPL
35006  * <script type="text/javascript">
35007  */
35008
35009 /**
35010  * @class Roo.menu.Separator
35011  * @extends Roo.menu.BaseItem
35012  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35013  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35014  * @constructor
35015  * @param {Object} config Configuration options
35016  */
35017 Roo.menu.Separator = function(config){
35018     Roo.menu.Separator.superclass.constructor.call(this, config);
35019 };
35020
35021 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35022     /**
35023      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35024      */
35025     itemCls : "x-menu-sep",
35026     /**
35027      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35028      */
35029     hideOnClick : false,
35030
35031     // private
35032     onRender : function(li){
35033         var s = document.createElement("span");
35034         s.className = this.itemCls;
35035         s.innerHTML = "&#160;";
35036         this.el = s;
35037         li.addClass("x-menu-sep-li");
35038         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35039     }
35040 });/*
35041  * Based on:
35042  * Ext JS Library 1.1.1
35043  * Copyright(c) 2006-2007, Ext JS, LLC.
35044  *
35045  * Originally Released Under LGPL - original licence link has changed is not relivant.
35046  *
35047  * Fork - LGPL
35048  * <script type="text/javascript">
35049  */
35050 /**
35051  * @class Roo.menu.Item
35052  * @extends Roo.menu.BaseItem
35053  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35054  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35055  * activation and click handling.
35056  * @constructor
35057  * Creates a new Item
35058  * @param {Object} config Configuration options
35059  */
35060 Roo.menu.Item = function(config){
35061     Roo.menu.Item.superclass.constructor.call(this, config);
35062     if(this.menu){
35063         this.menu = Roo.menu.MenuMgr.get(this.menu);
35064     }
35065 };
35066 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35067     
35068     /**
35069      * @cfg {String} text
35070      * The text to show on the menu item.
35071      */
35072     text: '',
35073      /**
35074      * @cfg {String} HTML to render in menu
35075      * The text to show on the menu item (HTML version).
35076      */
35077     html: '',
35078     /**
35079      * @cfg {String} icon
35080      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35081      */
35082     icon: undefined,
35083     /**
35084      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35085      */
35086     itemCls : "x-menu-item",
35087     /**
35088      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35089      */
35090     canActivate : true,
35091     /**
35092      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35093      */
35094     showDelay: 200,
35095     // doc'd in BaseItem
35096     hideDelay: 200,
35097
35098     // private
35099     ctype: "Roo.menu.Item",
35100     
35101     // private
35102     onRender : function(container, position){
35103         var el = document.createElement("a");
35104         el.hideFocus = true;
35105         el.unselectable = "on";
35106         el.href = this.href || "#";
35107         if(this.hrefTarget){
35108             el.target = this.hrefTarget;
35109         }
35110         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35111         
35112         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35113         
35114         el.innerHTML = String.format(
35115                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35116                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35117         this.el = el;
35118         Roo.menu.Item.superclass.onRender.call(this, container, position);
35119     },
35120
35121     /**
35122      * Sets the text to display in this menu item
35123      * @param {String} text The text to display
35124      * @param {Boolean} isHTML true to indicate text is pure html.
35125      */
35126     setText : function(text, isHTML){
35127         if (isHTML) {
35128             this.html = text;
35129         } else {
35130             this.text = text;
35131             this.html = '';
35132         }
35133         if(this.rendered){
35134             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35135      
35136             this.el.update(String.format(
35137                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35138                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35139             this.parentMenu.autoWidth();
35140         }
35141     },
35142
35143     // private
35144     handleClick : function(e){
35145         if(!this.href){ // if no link defined, stop the event automatically
35146             e.stopEvent();
35147         }
35148         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35149     },
35150
35151     // private
35152     activate : function(autoExpand){
35153         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35154             this.focus();
35155             if(autoExpand){
35156                 this.expandMenu();
35157             }
35158         }
35159         return true;
35160     },
35161
35162     // private
35163     shouldDeactivate : function(e){
35164         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35165             if(this.menu && this.menu.isVisible()){
35166                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35167             }
35168             return true;
35169         }
35170         return false;
35171     },
35172
35173     // private
35174     deactivate : function(){
35175         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35176         this.hideMenu();
35177     },
35178
35179     // private
35180     expandMenu : function(autoActivate){
35181         if(!this.disabled && this.menu){
35182             clearTimeout(this.hideTimer);
35183             delete this.hideTimer;
35184             if(!this.menu.isVisible() && !this.showTimer){
35185                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35186             }else if (this.menu.isVisible() && autoActivate){
35187                 this.menu.tryActivate(0, 1);
35188             }
35189         }
35190     },
35191
35192     // private
35193     deferExpand : function(autoActivate){
35194         delete this.showTimer;
35195         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35196         if(autoActivate){
35197             this.menu.tryActivate(0, 1);
35198         }
35199     },
35200
35201     // private
35202     hideMenu : function(){
35203         clearTimeout(this.showTimer);
35204         delete this.showTimer;
35205         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35206             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35207         }
35208     },
35209
35210     // private
35211     deferHide : function(){
35212         delete this.hideTimer;
35213         this.menu.hide();
35214     }
35215 });/*
35216  * Based on:
35217  * Ext JS Library 1.1.1
35218  * Copyright(c) 2006-2007, Ext JS, LLC.
35219  *
35220  * Originally Released Under LGPL - original licence link has changed is not relivant.
35221  *
35222  * Fork - LGPL
35223  * <script type="text/javascript">
35224  */
35225  
35226 /**
35227  * @class Roo.menu.CheckItem
35228  * @extends Roo.menu.Item
35229  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35230  * @constructor
35231  * Creates a new CheckItem
35232  * @param {Object} config Configuration options
35233  */
35234 Roo.menu.CheckItem = function(config){
35235     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35236     this.addEvents({
35237         /**
35238          * @event beforecheckchange
35239          * Fires before the checked value is set, providing an opportunity to cancel if needed
35240          * @param {Roo.menu.CheckItem} this
35241          * @param {Boolean} checked The new checked value that will be set
35242          */
35243         "beforecheckchange" : true,
35244         /**
35245          * @event checkchange
35246          * Fires after the checked value has been set
35247          * @param {Roo.menu.CheckItem} this
35248          * @param {Boolean} checked The checked value that was set
35249          */
35250         "checkchange" : true
35251     });
35252     if(this.checkHandler){
35253         this.on('checkchange', this.checkHandler, this.scope);
35254     }
35255 };
35256 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35257     /**
35258      * @cfg {String} group
35259      * All check items with the same group name will automatically be grouped into a single-select
35260      * radio button group (defaults to '')
35261      */
35262     /**
35263      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35264      */
35265     itemCls : "x-menu-item x-menu-check-item",
35266     /**
35267      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35268      */
35269     groupClass : "x-menu-group-item",
35270
35271     /**
35272      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35273      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35274      * initialized with checked = true will be rendered as checked.
35275      */
35276     checked: false,
35277
35278     // private
35279     ctype: "Roo.menu.CheckItem",
35280
35281     // private
35282     onRender : function(c){
35283         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35284         if(this.group){
35285             this.el.addClass(this.groupClass);
35286         }
35287         Roo.menu.MenuMgr.registerCheckable(this);
35288         if(this.checked){
35289             this.checked = false;
35290             this.setChecked(true, true);
35291         }
35292     },
35293
35294     // private
35295     destroy : function(){
35296         if(this.rendered){
35297             Roo.menu.MenuMgr.unregisterCheckable(this);
35298         }
35299         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35300     },
35301
35302     /**
35303      * Set the checked state of this item
35304      * @param {Boolean} checked The new checked value
35305      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35306      */
35307     setChecked : function(state, suppressEvent){
35308         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35309             if(this.container){
35310                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35311             }
35312             this.checked = state;
35313             if(suppressEvent !== true){
35314                 this.fireEvent("checkchange", this, state);
35315             }
35316         }
35317     },
35318
35319     // private
35320     handleClick : function(e){
35321        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35322            this.setChecked(!this.checked);
35323        }
35324        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35325     }
35326 });/*
35327  * Based on:
35328  * Ext JS Library 1.1.1
35329  * Copyright(c) 2006-2007, Ext JS, LLC.
35330  *
35331  * Originally Released Under LGPL - original licence link has changed is not relivant.
35332  *
35333  * Fork - LGPL
35334  * <script type="text/javascript">
35335  */
35336  
35337 /**
35338  * @class Roo.menu.DateItem
35339  * @extends Roo.menu.Adapter
35340  * A menu item that wraps the {@link Roo.DatPicker} component.
35341  * @constructor
35342  * Creates a new DateItem
35343  * @param {Object} config Configuration options
35344  */
35345 Roo.menu.DateItem = function(config){
35346     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35347     /** The Roo.DatePicker object @type Roo.DatePicker */
35348     this.picker = this.component;
35349     this.addEvents({select: true});
35350     
35351     this.picker.on("render", function(picker){
35352         picker.getEl().swallowEvent("click");
35353         picker.container.addClass("x-menu-date-item");
35354     });
35355
35356     this.picker.on("select", this.onSelect, this);
35357 };
35358
35359 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35360     // private
35361     onSelect : function(picker, date){
35362         this.fireEvent("select", this, date, picker);
35363         Roo.menu.DateItem.superclass.handleClick.call(this);
35364     }
35365 });/*
35366  * Based on:
35367  * Ext JS Library 1.1.1
35368  * Copyright(c) 2006-2007, Ext JS, LLC.
35369  *
35370  * Originally Released Under LGPL - original licence link has changed is not relivant.
35371  *
35372  * Fork - LGPL
35373  * <script type="text/javascript">
35374  */
35375  
35376 /**
35377  * @class Roo.menu.ColorItem
35378  * @extends Roo.menu.Adapter
35379  * A menu item that wraps the {@link Roo.ColorPalette} component.
35380  * @constructor
35381  * Creates a new ColorItem
35382  * @param {Object} config Configuration options
35383  */
35384 Roo.menu.ColorItem = function(config){
35385     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35386     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35387     this.palette = this.component;
35388     this.relayEvents(this.palette, ["select"]);
35389     if(this.selectHandler){
35390         this.on('select', this.selectHandler, this.scope);
35391     }
35392 };
35393 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35394  * Based on:
35395  * Ext JS Library 1.1.1
35396  * Copyright(c) 2006-2007, Ext JS, LLC.
35397  *
35398  * Originally Released Under LGPL - original licence link has changed is not relivant.
35399  *
35400  * Fork - LGPL
35401  * <script type="text/javascript">
35402  */
35403  
35404
35405 /**
35406  * @class Roo.menu.DateMenu
35407  * @extends Roo.menu.Menu
35408  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35409  * @constructor
35410  * Creates a new DateMenu
35411  * @param {Object} config Configuration options
35412  */
35413 Roo.menu.DateMenu = function(config){
35414     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35415     this.plain = true;
35416     var di = new Roo.menu.DateItem(config);
35417     this.add(di);
35418     /**
35419      * The {@link Roo.DatePicker} instance for this DateMenu
35420      * @type DatePicker
35421      */
35422     this.picker = di.picker;
35423     /**
35424      * @event select
35425      * @param {DatePicker} picker
35426      * @param {Date} date
35427      */
35428     this.relayEvents(di, ["select"]);
35429
35430     this.on('beforeshow', function(){
35431         if(this.picker){
35432             this.picker.hideMonthPicker(true);
35433         }
35434     }, this);
35435 };
35436 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35437     cls:'x-date-menu'
35438 });/*
35439  * Based on:
35440  * Ext JS Library 1.1.1
35441  * Copyright(c) 2006-2007, Ext JS, LLC.
35442  *
35443  * Originally Released Under LGPL - original licence link has changed is not relivant.
35444  *
35445  * Fork - LGPL
35446  * <script type="text/javascript">
35447  */
35448  
35449
35450 /**
35451  * @class Roo.menu.ColorMenu
35452  * @extends Roo.menu.Menu
35453  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35454  * @constructor
35455  * Creates a new ColorMenu
35456  * @param {Object} config Configuration options
35457  */
35458 Roo.menu.ColorMenu = function(config){
35459     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35460     this.plain = true;
35461     var ci = new Roo.menu.ColorItem(config);
35462     this.add(ci);
35463     /**
35464      * The {@link Roo.ColorPalette} instance for this ColorMenu
35465      * @type ColorPalette
35466      */
35467     this.palette = ci.palette;
35468     /**
35469      * @event select
35470      * @param {ColorPalette} palette
35471      * @param {String} color
35472      */
35473     this.relayEvents(ci, ["select"]);
35474 };
35475 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35476  * Based on:
35477  * Ext JS Library 1.1.1
35478  * Copyright(c) 2006-2007, Ext JS, LLC.
35479  *
35480  * Originally Released Under LGPL - original licence link has changed is not relivant.
35481  *
35482  * Fork - LGPL
35483  * <script type="text/javascript">
35484  */
35485  
35486 /**
35487  * @class Roo.form.Field
35488  * @extends Roo.BoxComponent
35489  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35490  * @constructor
35491  * Creates a new Field
35492  * @param {Object} config Configuration options
35493  */
35494 Roo.form.Field = function(config){
35495     Roo.form.Field.superclass.constructor.call(this, config);
35496 };
35497
35498 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35499     /**
35500      * @cfg {String} fieldLabel Label to use when rendering a form.
35501      */
35502        /**
35503      * @cfg {String} qtip Mouse over tip
35504      */
35505      
35506     /**
35507      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35508      */
35509     invalidClass : "x-form-invalid",
35510     /**
35511      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
35512      */
35513     invalidText : "The value in this field is invalid",
35514     /**
35515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35516      */
35517     focusClass : "x-form-focus",
35518     /**
35519      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35520       automatic validation (defaults to "keyup").
35521      */
35522     validationEvent : "keyup",
35523     /**
35524      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35525      */
35526     validateOnBlur : true,
35527     /**
35528      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35529      */
35530     validationDelay : 250,
35531     /**
35532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35533      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35534      */
35535     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35536     /**
35537      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35538      */
35539     fieldClass : "x-form-field",
35540     /**
35541      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35542      *<pre>
35543 Value         Description
35544 -----------   ----------------------------------------------------------------------
35545 qtip          Display a quick tip when the user hovers over the field
35546 title         Display a default browser title attribute popup
35547 under         Add a block div beneath the field containing the error text
35548 side          Add an error icon to the right of the field with a popup on hover
35549 [element id]  Add the error text directly to the innerHTML of the specified element
35550 </pre>
35551      */
35552     msgTarget : 'qtip',
35553     /**
35554      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35555      */
35556     msgFx : 'normal',
35557
35558     /**
35559      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
35560      */
35561     readOnly : false,
35562
35563     /**
35564      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35565      */
35566     disabled : false,
35567
35568     /**
35569      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35570      */
35571     inputType : undefined,
35572     
35573     /**
35574      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
35575          */
35576         tabIndex : undefined,
35577         
35578     // private
35579     isFormField : true,
35580
35581     // private
35582     hasFocus : false,
35583     /**
35584      * @property {Roo.Element} fieldEl
35585      * Element Containing the rendered Field (with label etc.)
35586      */
35587     /**
35588      * @cfg {Mixed} value A value to initialize this field with.
35589      */
35590     value : undefined,
35591
35592     /**
35593      * @cfg {String} name The field's HTML name attribute.
35594      */
35595     /**
35596      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35597      */
35598
35599         // private ??
35600         initComponent : function(){
35601         Roo.form.Field.superclass.initComponent.call(this);
35602         this.addEvents({
35603             /**
35604              * @event focus
35605              * Fires when this field receives input focus.
35606              * @param {Roo.form.Field} this
35607              */
35608             focus : true,
35609             /**
35610              * @event blur
35611              * Fires when this field loses input focus.
35612              * @param {Roo.form.Field} this
35613              */
35614             blur : true,
35615             /**
35616              * @event specialkey
35617              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35618              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35619              * @param {Roo.form.Field} this
35620              * @param {Roo.EventObject} e The event object
35621              */
35622             specialkey : true,
35623             /**
35624              * @event change
35625              * Fires just before the field blurs if the field value has changed.
35626              * @param {Roo.form.Field} this
35627              * @param {Mixed} newValue The new value
35628              * @param {Mixed} oldValue The original value
35629              */
35630             change : true,
35631             /**
35632              * @event invalid
35633              * Fires after the field has been marked as invalid.
35634              * @param {Roo.form.Field} this
35635              * @param {String} msg The validation message
35636              */
35637             invalid : true,
35638             /**
35639              * @event valid
35640              * Fires after the field has been validated with no errors.
35641              * @param {Roo.form.Field} this
35642              */
35643             valid : true,
35644              /**
35645              * @event keyup
35646              * Fires after the key up
35647              * @param {Roo.form.Field} this
35648              * @param {Roo.EventObject}  e The event Object
35649              */
35650             keyup : true
35651         });
35652     },
35653
35654     /**
35655      * Returns the name attribute of the field if available
35656      * @return {String} name The field name
35657      */
35658     getName: function(){
35659          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35660     },
35661
35662     // private
35663     onRender : function(ct, position){
35664         Roo.form.Field.superclass.onRender.call(this, ct, position);
35665         if(!this.el){
35666             var cfg = this.getAutoCreate();
35667             if(!cfg.name){
35668                 cfg.name = this.name || this.id;
35669             }
35670             if(this.inputType){
35671                 cfg.type = this.inputType;
35672             }
35673             this.el = ct.createChild(cfg, position);
35674         }
35675         var type = this.el.dom.type;
35676         if(type){
35677             if(type == 'password'){
35678                 type = 'text';
35679             }
35680             this.el.addClass('x-form-'+type);
35681         }
35682         if(this.readOnly){
35683             this.el.dom.readOnly = true;
35684         }
35685         if(this.tabIndex !== undefined){
35686             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35687         }
35688
35689         this.el.addClass([this.fieldClass, this.cls]);
35690         this.initValue();
35691     },
35692
35693     /**
35694      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35695      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35696      * @return {Roo.form.Field} this
35697      */
35698     applyTo : function(target){
35699         this.allowDomMove = false;
35700         this.el = Roo.get(target);
35701         this.render(this.el.dom.parentNode);
35702         return this;
35703     },
35704
35705     // private
35706     initValue : function(){
35707         if(this.value !== undefined){
35708             this.setValue(this.value);
35709         }else if(this.el.dom.value.length > 0){
35710             this.setValue(this.el.dom.value);
35711         }
35712     },
35713
35714     /**
35715      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35716      */
35717     isDirty : function() {
35718         if(this.disabled) {
35719             return false;
35720         }
35721         return String(this.getValue()) !== String(this.originalValue);
35722     },
35723
35724     // private
35725     afterRender : function(){
35726         Roo.form.Field.superclass.afterRender.call(this);
35727         this.initEvents();
35728     },
35729
35730     // private
35731     fireKey : function(e){
35732         //Roo.log('field ' + e.getKey());
35733         if(e.isNavKeyPress()){
35734             this.fireEvent("specialkey", this, e);
35735         }
35736     },
35737
35738     /**
35739      * Resets the current field value to the originally loaded value and clears any validation messages
35740      */
35741     reset : function(){
35742         this.setValue(this.originalValue);
35743         this.clearInvalid();
35744     },
35745
35746     // private
35747     initEvents : function(){
35748         // safari killled keypress - so keydown is now used..
35749         this.el.on("keydown" , this.fireKey,  this);
35750         this.el.on("focus", this.onFocus,  this);
35751         this.el.on("blur", this.onBlur,  this);
35752         this.el.relayEvent('keyup', this);
35753
35754         // reference to original value for reset
35755         this.originalValue = this.getValue();
35756     },
35757
35758     // private
35759     onFocus : function(){
35760         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35761             this.el.addClass(this.focusClass);
35762         }
35763         if(!this.hasFocus){
35764             this.hasFocus = true;
35765             this.startValue = this.getValue();
35766             this.fireEvent("focus", this);
35767         }
35768     },
35769
35770     beforeBlur : Roo.emptyFn,
35771
35772     // private
35773     onBlur : function(){
35774         this.beforeBlur();
35775         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35776             this.el.removeClass(this.focusClass);
35777         }
35778         this.hasFocus = false;
35779         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35780             this.validate();
35781         }
35782         var v = this.getValue();
35783         if(String(v) !== String(this.startValue)){
35784             this.fireEvent('change', this, v, this.startValue);
35785         }
35786         this.fireEvent("blur", this);
35787     },
35788
35789     /**
35790      * Returns whether or not the field value is currently valid
35791      * @param {Boolean} preventMark True to disable marking the field invalid
35792      * @return {Boolean} True if the value is valid, else false
35793      */
35794     isValid : function(preventMark){
35795         if(this.disabled){
35796             return true;
35797         }
35798         var restore = this.preventMark;
35799         this.preventMark = preventMark === true;
35800         var v = this.validateValue(this.processValue(this.getRawValue()));
35801         this.preventMark = restore;
35802         return v;
35803     },
35804
35805     /**
35806      * Validates the field value
35807      * @return {Boolean} True if the value is valid, else false
35808      */
35809     validate : function(){
35810         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35811             this.clearInvalid();
35812             return true;
35813         }
35814         return false;
35815     },
35816
35817     processValue : function(value){
35818         return value;
35819     },
35820
35821     // private
35822     // Subclasses should provide the validation implementation by overriding this
35823     validateValue : function(value){
35824         return true;
35825     },
35826
35827     /**
35828      * Mark this field as invalid
35829      * @param {String} msg The validation message
35830      */
35831     markInvalid : function(msg){
35832         if(!this.rendered || this.preventMark){ // not rendered
35833             return;
35834         }
35835         this.el.addClass(this.invalidClass);
35836         msg = msg || this.invalidText;
35837         switch(this.msgTarget){
35838             case 'qtip':
35839                 this.el.dom.qtip = msg;
35840                 this.el.dom.qclass = 'x-form-invalid-tip';
35841                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35842                     Roo.QuickTips.enable();
35843                 }
35844                 break;
35845             case 'title':
35846                 this.el.dom.title = msg;
35847                 break;
35848             case 'under':
35849                 if(!this.errorEl){
35850                     var elp = this.el.findParent('.x-form-element', 5, true);
35851                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35852                     this.errorEl.setWidth(elp.getWidth(true)-20);
35853                 }
35854                 this.errorEl.update(msg);
35855                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35856                 break;
35857             case 'side':
35858                 if(!this.errorIcon){
35859                     var elp = this.el.findParent('.x-form-element', 5, true);
35860                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35861                 }
35862                 this.alignErrorIcon();
35863                 this.errorIcon.dom.qtip = msg;
35864                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35865                 this.errorIcon.show();
35866                 this.on('resize', this.alignErrorIcon, this);
35867                 break;
35868             default:
35869                 var t = Roo.getDom(this.msgTarget);
35870                 t.innerHTML = msg;
35871                 t.style.display = this.msgDisplay;
35872                 break;
35873         }
35874         this.fireEvent('invalid', this, msg);
35875     },
35876
35877     // private
35878     alignErrorIcon : function(){
35879         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35880     },
35881
35882     /**
35883      * Clear any invalid styles/messages for this field
35884      */
35885     clearInvalid : function(){
35886         if(!this.rendered || this.preventMark){ // not rendered
35887             return;
35888         }
35889         this.el.removeClass(this.invalidClass);
35890         switch(this.msgTarget){
35891             case 'qtip':
35892                 this.el.dom.qtip = '';
35893                 break;
35894             case 'title':
35895                 this.el.dom.title = '';
35896                 break;
35897             case 'under':
35898                 if(this.errorEl){
35899                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35900                 }
35901                 break;
35902             case 'side':
35903                 if(this.errorIcon){
35904                     this.errorIcon.dom.qtip = '';
35905                     this.errorIcon.hide();
35906                     this.un('resize', this.alignErrorIcon, this);
35907                 }
35908                 break;
35909             default:
35910                 var t = Roo.getDom(this.msgTarget);
35911                 t.innerHTML = '';
35912                 t.style.display = 'none';
35913                 break;
35914         }
35915         this.fireEvent('valid', this);
35916     },
35917
35918     /**
35919      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35920      * @return {Mixed} value The field value
35921      */
35922     getRawValue : function(){
35923         var v = this.el.getValue();
35924         if(v === this.emptyText){
35925             v = '';
35926         }
35927         return v;
35928     },
35929
35930     /**
35931      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35932      * @return {Mixed} value The field value
35933      */
35934     getValue : function(){
35935         var v = this.el.getValue();
35936         if(v === this.emptyText || v === undefined){
35937             v = '';
35938         }
35939         return v;
35940     },
35941
35942     /**
35943      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35944      * @param {Mixed} value The value to set
35945      */
35946     setRawValue : function(v){
35947         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35948     },
35949
35950     /**
35951      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35952      * @param {Mixed} value The value to set
35953      */
35954     setValue : function(v){
35955         this.value = v;
35956         if(this.rendered){
35957             this.el.dom.value = (v === null || v === undefined ? '' : v);
35958             this.validate();
35959         }
35960     },
35961
35962     adjustSize : function(w, h){
35963         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35964         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35965         return s;
35966     },
35967
35968     adjustWidth : function(tag, w){
35969         tag = tag.toLowerCase();
35970         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35971             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35972                 if(tag == 'input'){
35973                     return w + 2;
35974                 }
35975                 if(tag = 'textarea'){
35976                     return w-2;
35977                 }
35978             }else if(Roo.isOpera){
35979                 if(tag == 'input'){
35980                     return w + 2;
35981                 }
35982                 if(tag = 'textarea'){
35983                     return w-2;
35984                 }
35985             }
35986         }
35987         return w;
35988     }
35989 });
35990
35991
35992 // anything other than normal should be considered experimental
35993 Roo.form.Field.msgFx = {
35994     normal : {
35995         show: function(msgEl, f){
35996             msgEl.setDisplayed('block');
35997         },
35998
35999         hide : function(msgEl, f){
36000             msgEl.setDisplayed(false).update('');
36001         }
36002     },
36003
36004     slide : {
36005         show: function(msgEl, f){
36006             msgEl.slideIn('t', {stopFx:true});
36007         },
36008
36009         hide : function(msgEl, f){
36010             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36011         }
36012     },
36013
36014     slideRight : {
36015         show: function(msgEl, f){
36016             msgEl.fixDisplay();
36017             msgEl.alignTo(f.el, 'tl-tr');
36018             msgEl.slideIn('l', {stopFx:true});
36019         },
36020
36021         hide : function(msgEl, f){
36022             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36023         }
36024     }
36025 };/*
36026  * Based on:
36027  * Ext JS Library 1.1.1
36028  * Copyright(c) 2006-2007, Ext JS, LLC.
36029  *
36030  * Originally Released Under LGPL - original licence link has changed is not relivant.
36031  *
36032  * Fork - LGPL
36033  * <script type="text/javascript">
36034  */
36035  
36036
36037 /**
36038  * @class Roo.form.TextField
36039  * @extends Roo.form.Field
36040  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36041  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36042  * @constructor
36043  * Creates a new TextField
36044  * @param {Object} config Configuration options
36045  */
36046 Roo.form.TextField = function(config){
36047     Roo.form.TextField.superclass.constructor.call(this, config);
36048     this.addEvents({
36049         /**
36050          * @event autosize
36051          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36052          * according to the default logic, but this event provides a hook for the developer to apply additional
36053          * logic at runtime to resize the field if needed.
36054              * @param {Roo.form.Field} this This text field
36055              * @param {Number} width The new field width
36056              */
36057         autosize : true
36058     });
36059 };
36060
36061 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36062     /**
36063      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36064      */
36065     grow : false,
36066     /**
36067      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36068      */
36069     growMin : 30,
36070     /**
36071      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36072      */
36073     growMax : 800,
36074     /**
36075      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36076      */
36077     vtype : null,
36078     /**
36079      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36080      */
36081     maskRe : null,
36082     /**
36083      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36084      */
36085     disableKeyFilter : false,
36086     /**
36087      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36088      */
36089     allowBlank : true,
36090     /**
36091      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36092      */
36093     minLength : 0,
36094     /**
36095      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36096      */
36097     maxLength : Number.MAX_VALUE,
36098     /**
36099      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36100      */
36101     minLengthText : "The minimum length for this field is {0}",
36102     /**
36103      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36104      */
36105     maxLengthText : "The maximum length for this field is {0}",
36106     /**
36107      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36108      */
36109     selectOnFocus : false,
36110     /**
36111      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36112      */
36113     blankText : "This field is required",
36114     /**
36115      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36116      * If available, this function will be called only after the basic validators all return true, and will be passed the
36117      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36118      */
36119     validator : null,
36120     /**
36121      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36122      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36123      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36124      */
36125     regex : null,
36126     /**
36127      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36128      */
36129     regexText : "",
36130     /**
36131      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36132      */
36133     emptyText : null,
36134     /**
36135      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36136      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36137      */
36138     emptyClass : 'x-form-empty-field',
36139
36140     // private
36141     initEvents : function(){
36142         Roo.form.TextField.superclass.initEvents.call(this);
36143         if(this.validationEvent == 'keyup'){
36144             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36145             this.el.on('keyup', this.filterValidation, this);
36146         }
36147         else if(this.validationEvent !== false){
36148             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36149         }
36150         if(this.selectOnFocus || this.emptyText){
36151             this.on("focus", this.preFocus, this);
36152             if(this.emptyText){
36153                 this.on('blur', this.postBlur, this);
36154                 this.applyEmptyText();
36155             }
36156         }
36157         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36158             this.el.on("keypress", this.filterKeys, this);
36159         }
36160         if(this.grow){
36161             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36162             this.el.on("click", this.autoSize,  this);
36163         }
36164     },
36165
36166     processValue : function(value){
36167         if(this.stripCharsRe){
36168             var newValue = value.replace(this.stripCharsRe, '');
36169             if(newValue !== value){
36170                 this.setRawValue(newValue);
36171                 return newValue;
36172             }
36173         }
36174         return value;
36175     },
36176
36177     filterValidation : function(e){
36178         if(!e.isNavKeyPress()){
36179             this.validationTask.delay(this.validationDelay);
36180         }
36181     },
36182
36183     // private
36184     onKeyUp : function(e){
36185         if(!e.isNavKeyPress()){
36186             this.autoSize();
36187         }
36188     },
36189
36190     /**
36191      * Resets the current field value to the originally-loaded value and clears any validation messages.
36192      * Also adds emptyText and emptyClass if the original value was blank.
36193      */
36194     reset : function(){
36195         Roo.form.TextField.superclass.reset.call(this);
36196         this.applyEmptyText();
36197     },
36198
36199     applyEmptyText : function(){
36200         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36201             this.setRawValue(this.emptyText);
36202             this.el.addClass(this.emptyClass);
36203         }
36204     },
36205
36206     // private
36207     preFocus : function(){
36208         if(this.emptyText){
36209             if(this.el.dom.value == this.emptyText){
36210                 this.setRawValue('');
36211             }
36212             this.el.removeClass(this.emptyClass);
36213         }
36214         if(this.selectOnFocus){
36215             this.el.dom.select();
36216         }
36217     },
36218
36219     // private
36220     postBlur : function(){
36221         this.applyEmptyText();
36222     },
36223
36224     // private
36225     filterKeys : function(e){
36226         var k = e.getKey();
36227         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36228             return;
36229         }
36230         var c = e.getCharCode(), cc = String.fromCharCode(c);
36231         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36232             return;
36233         }
36234         if(!this.maskRe.test(cc)){
36235             e.stopEvent();
36236         }
36237     },
36238
36239     setValue : function(v){
36240         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36241             this.el.removeClass(this.emptyClass);
36242         }
36243         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36244         this.applyEmptyText();
36245         this.autoSize();
36246     },
36247
36248     /**
36249      * Validates a value according to the field's validation rules and marks the field as invalid
36250      * if the validation fails
36251      * @param {Mixed} value The value to validate
36252      * @return {Boolean} True if the value is valid, else false
36253      */
36254     validateValue : function(value){
36255         if(value.length < 1 || value === this.emptyText){ // if it's blank
36256              if(this.allowBlank){
36257                 this.clearInvalid();
36258                 return true;
36259              }else{
36260                 this.markInvalid(this.blankText);
36261                 return false;
36262              }
36263         }
36264         if(value.length < this.minLength){
36265             this.markInvalid(String.format(this.minLengthText, this.minLength));
36266             return false;
36267         }
36268         if(value.length > this.maxLength){
36269             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36270             return false;
36271         }
36272         if(this.vtype){
36273             var vt = Roo.form.VTypes;
36274             if(!vt[this.vtype](value, this)){
36275                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36276                 return false;
36277             }
36278         }
36279         if(typeof this.validator == "function"){
36280             var msg = this.validator(value);
36281             if(msg !== true){
36282                 this.markInvalid(msg);
36283                 return false;
36284             }
36285         }
36286         if(this.regex && !this.regex.test(value)){
36287             this.markInvalid(this.regexText);
36288             return false;
36289         }
36290         return true;
36291     },
36292
36293     /**
36294      * Selects text in this field
36295      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36296      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36297      */
36298     selectText : function(start, end){
36299         var v = this.getRawValue();
36300         if(v.length > 0){
36301             start = start === undefined ? 0 : start;
36302             end = end === undefined ? v.length : end;
36303             var d = this.el.dom;
36304             if(d.setSelectionRange){
36305                 d.setSelectionRange(start, end);
36306             }else if(d.createTextRange){
36307                 var range = d.createTextRange();
36308                 range.moveStart("character", start);
36309                 range.moveEnd("character", v.length-end);
36310                 range.select();
36311             }
36312         }
36313     },
36314
36315     /**
36316      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36317      * This only takes effect if grow = true, and fires the autosize event.
36318      */
36319     autoSize : function(){
36320         if(!this.grow || !this.rendered){
36321             return;
36322         }
36323         if(!this.metrics){
36324             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36325         }
36326         var el = this.el;
36327         var v = el.dom.value;
36328         var d = document.createElement('div');
36329         d.appendChild(document.createTextNode(v));
36330         v = d.innerHTML;
36331         d = null;
36332         v += "&#160;";
36333         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36334         this.el.setWidth(w);
36335         this.fireEvent("autosize", this, w);
36336     }
36337 });/*
36338  * Based on:
36339  * Ext JS Library 1.1.1
36340  * Copyright(c) 2006-2007, Ext JS, LLC.
36341  *
36342  * Originally Released Under LGPL - original licence link has changed is not relivant.
36343  *
36344  * Fork - LGPL
36345  * <script type="text/javascript">
36346  */
36347  
36348 /**
36349  * @class Roo.form.Hidden
36350  * @extends Roo.form.TextField
36351  * Simple Hidden element used on forms 
36352  * 
36353  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36354  * 
36355  * @constructor
36356  * Creates a new Hidden form element.
36357  * @param {Object} config Configuration options
36358  */
36359
36360
36361
36362 // easy hidden field...
36363 Roo.form.Hidden = function(config){
36364     Roo.form.Hidden.superclass.constructor.call(this, config);
36365 };
36366   
36367 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36368     fieldLabel:      '',
36369     inputType:      'hidden',
36370     width:          50,
36371     allowBlank:     true,
36372     labelSeparator: '',
36373     hidden:         true,
36374     itemCls :       'x-form-item-display-none'
36375
36376
36377 });
36378
36379
36380 /*
36381  * Based on:
36382  * Ext JS Library 1.1.1
36383  * Copyright(c) 2006-2007, Ext JS, LLC.
36384  *
36385  * Originally Released Under LGPL - original licence link has changed is not relivant.
36386  *
36387  * Fork - LGPL
36388  * <script type="text/javascript">
36389  */
36390  
36391 /**
36392  * @class Roo.form.TriggerField
36393  * @extends Roo.form.TextField
36394  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36395  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36396  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36397  * for which you can provide a custom implementation.  For example:
36398  * <pre><code>
36399 var trigger = new Roo.form.TriggerField();
36400 trigger.onTriggerClick = myTriggerFn;
36401 trigger.applyTo('my-field');
36402 </code></pre>
36403  *
36404  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36405  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36406  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36407  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36408  * @constructor
36409  * Create a new TriggerField.
36410  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36411  * to the base TextField)
36412  */
36413 Roo.form.TriggerField = function(config){
36414     this.mimicing = false;
36415     Roo.form.TriggerField.superclass.constructor.call(this, config);
36416 };
36417
36418 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36419     /**
36420      * @cfg {String} triggerClass A CSS class to apply to the trigger
36421      */
36422     /**
36423      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36424      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36425      */
36426     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36427     /**
36428      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36429      */
36430     hideTrigger:false,
36431
36432     /** @cfg {Boolean} grow @hide */
36433     /** @cfg {Number} growMin @hide */
36434     /** @cfg {Number} growMax @hide */
36435
36436     /**
36437      * @hide 
36438      * @method
36439      */
36440     autoSize: Roo.emptyFn,
36441     // private
36442     monitorTab : true,
36443     // private
36444     deferHeight : true,
36445
36446     
36447     actionMode : 'wrap',
36448     // private
36449     onResize : function(w, h){
36450         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36451         if(typeof w == 'number'){
36452             var x = w - this.trigger.getWidth();
36453             this.el.setWidth(this.adjustWidth('input', x));
36454             this.trigger.setStyle('left', x+'px');
36455         }
36456     },
36457
36458     // private
36459     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36460
36461     // private
36462     getResizeEl : function(){
36463         return this.wrap;
36464     },
36465
36466     // private
36467     getPositionEl : function(){
36468         return this.wrap;
36469     },
36470
36471     // private
36472     alignErrorIcon : function(){
36473         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36474     },
36475
36476     // private
36477     onRender : function(ct, position){
36478         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36479         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36480         this.trigger = this.wrap.createChild(this.triggerConfig ||
36481                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36482         if(this.hideTrigger){
36483             this.trigger.setDisplayed(false);
36484         }
36485         this.initTrigger();
36486         if(!this.width){
36487             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36488         }
36489     },
36490
36491     // private
36492     initTrigger : function(){
36493         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36494         this.trigger.addClassOnOver('x-form-trigger-over');
36495         this.trigger.addClassOnClick('x-form-trigger-click');
36496     },
36497
36498     // private
36499     onDestroy : function(){
36500         if(this.trigger){
36501             this.trigger.removeAllListeners();
36502             this.trigger.remove();
36503         }
36504         if(this.wrap){
36505             this.wrap.remove();
36506         }
36507         Roo.form.TriggerField.superclass.onDestroy.call(this);
36508     },
36509
36510     // private
36511     onFocus : function(){
36512         Roo.form.TriggerField.superclass.onFocus.call(this);
36513         if(!this.mimicing){
36514             this.wrap.addClass('x-trigger-wrap-focus');
36515             this.mimicing = true;
36516             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36517             if(this.monitorTab){
36518                 this.el.on("keydown", this.checkTab, this);
36519             }
36520         }
36521     },
36522
36523     // private
36524     checkTab : function(e){
36525         if(e.getKey() == e.TAB){
36526             this.triggerBlur();
36527         }
36528     },
36529
36530     // private
36531     onBlur : function(){
36532         // do nothing
36533     },
36534
36535     // private
36536     mimicBlur : function(e, t){
36537         if(!this.wrap.contains(t) && this.validateBlur()){
36538             this.triggerBlur();
36539         }
36540     },
36541
36542     // private
36543     triggerBlur : function(){
36544         this.mimicing = false;
36545         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36546         if(this.monitorTab){
36547             this.el.un("keydown", this.checkTab, this);
36548         }
36549         this.wrap.removeClass('x-trigger-wrap-focus');
36550         Roo.form.TriggerField.superclass.onBlur.call(this);
36551     },
36552
36553     // private
36554     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36555     validateBlur : function(e, t){
36556         return true;
36557     },
36558
36559     // private
36560     onDisable : function(){
36561         Roo.form.TriggerField.superclass.onDisable.call(this);
36562         if(this.wrap){
36563             this.wrap.addClass('x-item-disabled');
36564         }
36565     },
36566
36567     // private
36568     onEnable : function(){
36569         Roo.form.TriggerField.superclass.onEnable.call(this);
36570         if(this.wrap){
36571             this.wrap.removeClass('x-item-disabled');
36572         }
36573     },
36574
36575     // private
36576     onShow : function(){
36577         var ae = this.getActionEl();
36578         
36579         if(ae){
36580             ae.dom.style.display = '';
36581             ae.dom.style.visibility = 'visible';
36582         }
36583     },
36584
36585     // private
36586     
36587     onHide : function(){
36588         var ae = this.getActionEl();
36589         ae.dom.style.display = 'none';
36590     },
36591
36592     /**
36593      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36594      * by an implementing function.
36595      * @method
36596      * @param {EventObject} e
36597      */
36598     onTriggerClick : Roo.emptyFn
36599 });
36600
36601 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36602 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36603 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36604 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36605     initComponent : function(){
36606         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36607
36608         this.triggerConfig = {
36609             tag:'span', cls:'x-form-twin-triggers', cn:[
36610             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36611             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36612         ]};
36613     },
36614
36615     getTrigger : function(index){
36616         return this.triggers[index];
36617     },
36618
36619     initTrigger : function(){
36620         var ts = this.trigger.select('.x-form-trigger', true);
36621         this.wrap.setStyle('overflow', 'hidden');
36622         var triggerField = this;
36623         ts.each(function(t, all, index){
36624             t.hide = function(){
36625                 var w = triggerField.wrap.getWidth();
36626                 this.dom.style.display = 'none';
36627                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36628             };
36629             t.show = function(){
36630                 var w = triggerField.wrap.getWidth();
36631                 this.dom.style.display = '';
36632                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36633             };
36634             var triggerIndex = 'Trigger'+(index+1);
36635
36636             if(this['hide'+triggerIndex]){
36637                 t.dom.style.display = 'none';
36638             }
36639             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36640             t.addClassOnOver('x-form-trigger-over');
36641             t.addClassOnClick('x-form-trigger-click');
36642         }, this);
36643         this.triggers = ts.elements;
36644     },
36645
36646     onTrigger1Click : Roo.emptyFn,
36647     onTrigger2Click : Roo.emptyFn
36648 });/*
36649  * Based on:
36650  * Ext JS Library 1.1.1
36651  * Copyright(c) 2006-2007, Ext JS, LLC.
36652  *
36653  * Originally Released Under LGPL - original licence link has changed is not relivant.
36654  *
36655  * Fork - LGPL
36656  * <script type="text/javascript">
36657  */
36658  
36659 /**
36660  * @class Roo.form.TextArea
36661  * @extends Roo.form.TextField
36662  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36663  * support for auto-sizing.
36664  * @constructor
36665  * Creates a new TextArea
36666  * @param {Object} config Configuration options
36667  */
36668 Roo.form.TextArea = function(config){
36669     Roo.form.TextArea.superclass.constructor.call(this, config);
36670     // these are provided exchanges for backwards compat
36671     // minHeight/maxHeight were replaced by growMin/growMax to be
36672     // compatible with TextField growing config values
36673     if(this.minHeight !== undefined){
36674         this.growMin = this.minHeight;
36675     }
36676     if(this.maxHeight !== undefined){
36677         this.growMax = this.maxHeight;
36678     }
36679 };
36680
36681 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36682     /**
36683      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36684      */
36685     growMin : 60,
36686     /**
36687      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36688      */
36689     growMax: 1000,
36690     /**
36691      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36692      * in the field (equivalent to setting overflow: hidden, defaults to false)
36693      */
36694     preventScrollbars: false,
36695     /**
36696      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36697      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36698      */
36699
36700     // private
36701     onRender : function(ct, position){
36702         if(!this.el){
36703             this.defaultAutoCreate = {
36704                 tag: "textarea",
36705                 style:"width:300px;height:60px;",
36706                 autocomplete: "off"
36707             };
36708         }
36709         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36710         if(this.grow){
36711             this.textSizeEl = Roo.DomHelper.append(document.body, {
36712                 tag: "pre", cls: "x-form-grow-sizer"
36713             });
36714             if(this.preventScrollbars){
36715                 this.el.setStyle("overflow", "hidden");
36716             }
36717             this.el.setHeight(this.growMin);
36718         }
36719     },
36720
36721     onDestroy : function(){
36722         if(this.textSizeEl){
36723             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36724         }
36725         Roo.form.TextArea.superclass.onDestroy.call(this);
36726     },
36727
36728     // private
36729     onKeyUp : function(e){
36730         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36731             this.autoSize();
36732         }
36733     },
36734
36735     /**
36736      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36737      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36738      */
36739     autoSize : function(){
36740         if(!this.grow || !this.textSizeEl){
36741             return;
36742         }
36743         var el = this.el;
36744         var v = el.dom.value;
36745         var ts = this.textSizeEl;
36746
36747         ts.innerHTML = '';
36748         ts.appendChild(document.createTextNode(v));
36749         v = ts.innerHTML;
36750
36751         Roo.fly(ts).setWidth(this.el.getWidth());
36752         if(v.length < 1){
36753             v = "&#160;&#160;";
36754         }else{
36755             if(Roo.isIE){
36756                 v = v.replace(/\n/g, '<p>&#160;</p>');
36757             }
36758             v += "&#160;\n&#160;";
36759         }
36760         ts.innerHTML = v;
36761         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36762         if(h != this.lastHeight){
36763             this.lastHeight = h;
36764             this.el.setHeight(h);
36765             this.fireEvent("autosize", this, h);
36766         }
36767     }
36768 });/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778  
36779
36780 /**
36781  * @class Roo.form.NumberField
36782  * @extends Roo.form.TextField
36783  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36784  * @constructor
36785  * Creates a new NumberField
36786  * @param {Object} config Configuration options
36787  */
36788 Roo.form.NumberField = function(config){
36789     Roo.form.NumberField.superclass.constructor.call(this, config);
36790 };
36791
36792 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36793     /**
36794      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36795      */
36796     fieldClass: "x-form-field x-form-num-field",
36797     /**
36798      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36799      */
36800     allowDecimals : true,
36801     /**
36802      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36803      */
36804     decimalSeparator : ".",
36805     /**
36806      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36807      */
36808     decimalPrecision : 2,
36809     /**
36810      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36811      */
36812     allowNegative : true,
36813     /**
36814      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36815      */
36816     minValue : Number.NEGATIVE_INFINITY,
36817     /**
36818      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36819      */
36820     maxValue : Number.MAX_VALUE,
36821     /**
36822      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36823      */
36824     minText : "The minimum value for this field is {0}",
36825     /**
36826      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36827      */
36828     maxText : "The maximum value for this field is {0}",
36829     /**
36830      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36831      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36832      */
36833     nanText : "{0} is not a valid number",
36834
36835     // private
36836     initEvents : function(){
36837         Roo.form.NumberField.superclass.initEvents.call(this);
36838         var allowed = "0123456789";
36839         if(this.allowDecimals){
36840             allowed += this.decimalSeparator;
36841         }
36842         if(this.allowNegative){
36843             allowed += "-";
36844         }
36845         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36846         var keyPress = function(e){
36847             var k = e.getKey();
36848             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36849                 return;
36850             }
36851             var c = e.getCharCode();
36852             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36853                 e.stopEvent();
36854             }
36855         };
36856         this.el.on("keypress", keyPress, this);
36857     },
36858
36859     // private
36860     validateValue : function(value){
36861         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36862             return false;
36863         }
36864         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36865              return true;
36866         }
36867         var num = this.parseValue(value);
36868         if(isNaN(num)){
36869             this.markInvalid(String.format(this.nanText, value));
36870             return false;
36871         }
36872         if(num < this.minValue){
36873             this.markInvalid(String.format(this.minText, this.minValue));
36874             return false;
36875         }
36876         if(num > this.maxValue){
36877             this.markInvalid(String.format(this.maxText, this.maxValue));
36878             return false;
36879         }
36880         return true;
36881     },
36882
36883     getValue : function(){
36884         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36885     },
36886
36887     // private
36888     parseValue : function(value){
36889         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36890         return isNaN(value) ? '' : value;
36891     },
36892
36893     // private
36894     fixPrecision : function(value){
36895         var nan = isNaN(value);
36896         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36897             return nan ? '' : value;
36898         }
36899         return parseFloat(value).toFixed(this.decimalPrecision);
36900     },
36901
36902     setValue : function(v){
36903         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36904     },
36905
36906     // private
36907     decimalPrecisionFcn : function(v){
36908         return Math.floor(v);
36909     },
36910
36911     beforeBlur : function(){
36912         var v = this.parseValue(this.getRawValue());
36913         if(v){
36914             this.setValue(this.fixPrecision(v));
36915         }
36916     }
36917 });/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927  
36928 /**
36929  * @class Roo.form.DateField
36930  * @extends Roo.form.TriggerField
36931  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36932 * @constructor
36933 * Create a new DateField
36934 * @param {Object} config
36935  */
36936 Roo.form.DateField = function(config){
36937     Roo.form.DateField.superclass.constructor.call(this, config);
36938     
36939       this.addEvents({
36940          
36941         /**
36942          * @event select
36943          * Fires when a date is selected
36944              * @param {Roo.form.DateField} combo This combo box
36945              * @param {Date} date The date selected
36946              */
36947         'select' : true
36948          
36949     });
36950     
36951     
36952     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36953     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36954     this.ddMatch = null;
36955     if(this.disabledDates){
36956         var dd = this.disabledDates;
36957         var re = "(?:";
36958         for(var i = 0; i < dd.length; i++){
36959             re += dd[i];
36960             if(i != dd.length-1) re += "|";
36961         }
36962         this.ddMatch = new RegExp(re + ")");
36963     }
36964 };
36965
36966 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36967     /**
36968      * @cfg {String} format
36969      * The default date format string which can be overriden for localization support.  The format must be
36970      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36971      */
36972     format : "m/d/y",
36973     /**
36974      * @cfg {String} altFormats
36975      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36976      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36977      */
36978     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36979     /**
36980      * @cfg {Array} disabledDays
36981      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36982      */
36983     disabledDays : null,
36984     /**
36985      * @cfg {String} disabledDaysText
36986      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36987      */
36988     disabledDaysText : "Disabled",
36989     /**
36990      * @cfg {Array} disabledDates
36991      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36992      * expression so they are very powerful. Some examples:
36993      * <ul>
36994      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36995      * <li>["03/08", "09/16"] would disable those days for every year</li>
36996      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36997      * <li>["03/../2006"] would disable every day in March 2006</li>
36998      * <li>["^03"] would disable every day in every March</li>
36999      * </ul>
37000      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37001      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37002      */
37003     disabledDates : null,
37004     /**
37005      * @cfg {String} disabledDatesText
37006      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37007      */
37008     disabledDatesText : "Disabled",
37009     /**
37010      * @cfg {Date/String} minValue
37011      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37012      * valid format (defaults to null).
37013      */
37014     minValue : null,
37015     /**
37016      * @cfg {Date/String} maxValue
37017      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37018      * valid format (defaults to null).
37019      */
37020     maxValue : null,
37021     /**
37022      * @cfg {String} minText
37023      * The error text to display when the date in the cell is before minValue (defaults to
37024      * 'The date in this field must be after {minValue}').
37025      */
37026     minText : "The date in this field must be equal to or after {0}",
37027     /**
37028      * @cfg {String} maxText
37029      * The error text to display when the date in the cell is after maxValue (defaults to
37030      * 'The date in this field must be before {maxValue}').
37031      */
37032     maxText : "The date in this field must be equal to or before {0}",
37033     /**
37034      * @cfg {String} invalidText
37035      * The error text to display when the date in the field is invalid (defaults to
37036      * '{value} is not a valid date - it must be in the format {format}').
37037      */
37038     invalidText : "{0} is not a valid date - it must be in the format {1}",
37039     /**
37040      * @cfg {String} triggerClass
37041      * An additional CSS class used to style the trigger button.  The trigger will always get the
37042      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37043      * which displays a calendar icon).
37044      */
37045     triggerClass : 'x-form-date-trigger',
37046     
37047
37048     /**
37049      * @cfg {bool} useIso
37050      * if enabled, then the date field will use a hidden field to store the 
37051      * real value as iso formated date. default (false)
37052      */ 
37053     useIso : false,
37054     /**
37055      * @cfg {String/Object} autoCreate
37056      * A DomHelper element spec, or true for a default element spec (defaults to
37057      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37058      */ 
37059     // private
37060     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37061     
37062     // private
37063     hiddenField: false,
37064     
37065     onRender : function(ct, position)
37066     {
37067         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37068         if (this.useIso) {
37069             this.el.dom.removeAttribute('name'); 
37070             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37071                     'before', true);
37072             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37073             // prevent input submission
37074             this.hiddenName = this.name;
37075         }
37076             
37077             
37078     },
37079     
37080     // private
37081     validateValue : function(value)
37082     {
37083         value = this.formatDate(value);
37084         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37085             return false;
37086         }
37087         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37088              return true;
37089         }
37090         var svalue = value;
37091         value = this.parseDate(value);
37092         if(!value){
37093             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37094             return false;
37095         }
37096         var time = value.getTime();
37097         if(this.minValue && time < this.minValue.getTime()){
37098             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37099             return false;
37100         }
37101         if(this.maxValue && time > this.maxValue.getTime()){
37102             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37103             return false;
37104         }
37105         if(this.disabledDays){
37106             var day = value.getDay();
37107             for(var i = 0; i < this.disabledDays.length; i++) {
37108                 if(day === this.disabledDays[i]){
37109                     this.markInvalid(this.disabledDaysText);
37110                     return false;
37111                 }
37112             }
37113         }
37114         var fvalue = this.formatDate(value);
37115         if(this.ddMatch && this.ddMatch.test(fvalue)){
37116             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37117             return false;
37118         }
37119         return true;
37120     },
37121
37122     // private
37123     // Provides logic to override the default TriggerField.validateBlur which just returns true
37124     validateBlur : function(){
37125         return !this.menu || !this.menu.isVisible();
37126     },
37127
37128     /**
37129      * Returns the current date value of the date field.
37130      * @return {Date} The date value
37131      */
37132     getValue : function(){
37133         
37134         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37135     },
37136
37137     /**
37138      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37139      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37140      * (the default format used is "m/d/y").
37141      * <br />Usage:
37142      * <pre><code>
37143 //All of these calls set the same date value (May 4, 2006)
37144
37145 //Pass a date object:
37146 var dt = new Date('5/4/06');
37147 dateField.setValue(dt);
37148
37149 //Pass a date string (default format):
37150 dateField.setValue('5/4/06');
37151
37152 //Pass a date string (custom format):
37153 dateField.format = 'Y-m-d';
37154 dateField.setValue('2006-5-4');
37155 </code></pre>
37156      * @param {String/Date} date The date or valid date string
37157      */
37158     setValue : function(date){
37159         if (this.hiddenField) {
37160             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37161         }
37162         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37163     },
37164
37165     // private
37166     parseDate : function(value){
37167         if(!value || value instanceof Date){
37168             return value;
37169         }
37170         var v = Date.parseDate(value, this.format);
37171         if(!v && this.altFormats){
37172             if(!this.altFormatsArray){
37173                 this.altFormatsArray = this.altFormats.split("|");
37174             }
37175             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37176                 v = Date.parseDate(value, this.altFormatsArray[i]);
37177             }
37178         }
37179         return v;
37180     },
37181
37182     // private
37183     formatDate : function(date, fmt){
37184         return (!date || !(date instanceof Date)) ?
37185                date : date.dateFormat(fmt || this.format);
37186     },
37187
37188     // private
37189     menuListeners : {
37190         select: function(m, d){
37191             this.setValue(d);
37192             this.fireEvent('select', this, d);
37193         },
37194         show : function(){ // retain focus styling
37195             this.onFocus();
37196         },
37197         hide : function(){
37198             this.focus.defer(10, this);
37199             var ml = this.menuListeners;
37200             this.menu.un("select", ml.select,  this);
37201             this.menu.un("show", ml.show,  this);
37202             this.menu.un("hide", ml.hide,  this);
37203         }
37204     },
37205
37206     // private
37207     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37208     onTriggerClick : function(){
37209         if(this.disabled){
37210             return;
37211         }
37212         if(this.menu == null){
37213             this.menu = new Roo.menu.DateMenu();
37214         }
37215         Roo.apply(this.menu.picker,  {
37216             showClear: this.allowBlank,
37217             minDate : this.minValue,
37218             maxDate : this.maxValue,
37219             disabledDatesRE : this.ddMatch,
37220             disabledDatesText : this.disabledDatesText,
37221             disabledDays : this.disabledDays,
37222             disabledDaysText : this.disabledDaysText,
37223             format : this.format,
37224             minText : String.format(this.minText, this.formatDate(this.minValue)),
37225             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37226         });
37227         this.menu.on(Roo.apply({}, this.menuListeners, {
37228             scope:this
37229         }));
37230         this.menu.picker.setValue(this.getValue() || new Date());
37231         this.menu.show(this.el, "tl-bl?");
37232     },
37233
37234     beforeBlur : function(){
37235         var v = this.parseDate(this.getRawValue());
37236         if(v){
37237             this.setValue(v);
37238         }
37239     }
37240
37241     /** @cfg {Boolean} grow @hide */
37242     /** @cfg {Number} growMin @hide */
37243     /** @cfg {Number} growMax @hide */
37244     /**
37245      * @hide
37246      * @method autoSize
37247      */
37248 });/*
37249  * Based on:
37250  * Ext JS Library 1.1.1
37251  * Copyright(c) 2006-2007, Ext JS, LLC.
37252  *
37253  * Originally Released Under LGPL - original licence link has changed is not relivant.
37254  *
37255  * Fork - LGPL
37256  * <script type="text/javascript">
37257  */
37258  
37259
37260 /**
37261  * @class Roo.form.ComboBox
37262  * @extends Roo.form.TriggerField
37263  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37264  * @constructor
37265  * Create a new ComboBox.
37266  * @param {Object} config Configuration options
37267  */
37268 Roo.form.ComboBox = function(config){
37269     Roo.form.ComboBox.superclass.constructor.call(this, config);
37270     this.addEvents({
37271         /**
37272          * @event expand
37273          * Fires when the dropdown list is expanded
37274              * @param {Roo.form.ComboBox} combo This combo box
37275              */
37276         'expand' : true,
37277         /**
37278          * @event collapse
37279          * Fires when the dropdown list is collapsed
37280              * @param {Roo.form.ComboBox} combo This combo box
37281              */
37282         'collapse' : true,
37283         /**
37284          * @event beforeselect
37285          * Fires before a list item is selected. Return false to cancel the selection.
37286              * @param {Roo.form.ComboBox} combo This combo box
37287              * @param {Roo.data.Record} record The data record returned from the underlying store
37288              * @param {Number} index The index of the selected item in the dropdown list
37289              */
37290         'beforeselect' : true,
37291         /**
37292          * @event select
37293          * Fires when a list item is selected
37294              * @param {Roo.form.ComboBox} combo This combo box
37295              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37296              * @param {Number} index The index of the selected item in the dropdown list
37297              */
37298         'select' : true,
37299         /**
37300          * @event beforequery
37301          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37302          * The event object passed has these properties:
37303              * @param {Roo.form.ComboBox} combo This combo box
37304              * @param {String} query The query
37305              * @param {Boolean} forceAll true to force "all" query
37306              * @param {Boolean} cancel true to cancel the query
37307              * @param {Object} e The query event object
37308              */
37309         'beforequery': true,
37310          /**
37311          * @event add
37312          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37313              * @param {Roo.form.ComboBox} combo This combo box
37314              */
37315         'add' : true,
37316         /**
37317          * @event edit
37318          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37319              * @param {Roo.form.ComboBox} combo This combo box
37320              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37321              */
37322         'edit' : true
37323         
37324         
37325     });
37326     if(this.transform){
37327         this.allowDomMove = false;
37328         var s = Roo.getDom(this.transform);
37329         if(!this.hiddenName){
37330             this.hiddenName = s.name;
37331         }
37332         if(!this.store){
37333             this.mode = 'local';
37334             var d = [], opts = s.options;
37335             for(var i = 0, len = opts.length;i < len; i++){
37336                 var o = opts[i];
37337                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37338                 if(o.selected) {
37339                     this.value = value;
37340                 }
37341                 d.push([value, o.text]);
37342             }
37343             this.store = new Roo.data.SimpleStore({
37344                 'id': 0,
37345                 fields: ['value', 'text'],
37346                 data : d
37347             });
37348             this.valueField = 'value';
37349             this.displayField = 'text';
37350         }
37351         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37352         if(!this.lazyRender){
37353             this.target = true;
37354             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37355             s.parentNode.removeChild(s); // remove it
37356             this.render(this.el.parentNode);
37357         }else{
37358             s.parentNode.removeChild(s); // remove it
37359         }
37360
37361     }
37362     if (this.store) {
37363         this.store = Roo.factory(this.store, Roo.data);
37364     }
37365     
37366     this.selectedIndex = -1;
37367     if(this.mode == 'local'){
37368         if(config.queryDelay === undefined){
37369             this.queryDelay = 10;
37370         }
37371         if(config.minChars === undefined){
37372             this.minChars = 0;
37373         }
37374     }
37375 };
37376
37377 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37378     /**
37379      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37380      */
37381     /**
37382      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37383      * rendering into an Roo.Editor, defaults to false)
37384      */
37385     /**
37386      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37387      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37388      */
37389     /**
37390      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37391      */
37392     /**
37393      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37394      * the dropdown list (defaults to undefined, with no header element)
37395      */
37396
37397      /**
37398      * @cfg {String/Roo.Template} tpl The template to use to render the output
37399      */
37400      
37401     // private
37402     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37403     /**
37404      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37405      */
37406     listWidth: undefined,
37407     /**
37408      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37409      * mode = 'remote' or 'text' if mode = 'local')
37410      */
37411     displayField: undefined,
37412     /**
37413      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37414      * mode = 'remote' or 'value' if mode = 'local'). 
37415      * Note: use of a valueField requires the user make a selection
37416      * in order for a value to be mapped.
37417      */
37418     valueField: undefined,
37419     /**
37420      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37421      * field's data value (defaults to the underlying DOM element's name)
37422      */
37423     hiddenName: undefined,
37424     /**
37425      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37426      */
37427     listClass: '',
37428     /**
37429      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37430      */
37431     selectedClass: 'x-combo-selected',
37432     /**
37433      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37434      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37435      * which displays a downward arrow icon).
37436      */
37437     triggerClass : 'x-form-arrow-trigger',
37438     /**
37439      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37440      */
37441     shadow:'sides',
37442     /**
37443      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37444      * anchor positions (defaults to 'tl-bl')
37445      */
37446     listAlign: 'tl-bl?',
37447     /**
37448      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37449      */
37450     maxHeight: 300,
37451     /**
37452      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37453      * query specified by the allQuery config option (defaults to 'query')
37454      */
37455     triggerAction: 'query',
37456     /**
37457      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37458      * (defaults to 4, does not apply if editable = false)
37459      */
37460     minChars : 4,
37461     /**
37462      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37463      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37464      */
37465     typeAhead: false,
37466     /**
37467      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37468      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37469      */
37470     queryDelay: 500,
37471     /**
37472      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37473      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37474      */
37475     pageSize: 0,
37476     /**
37477      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37478      * when editable = true (defaults to false)
37479      */
37480     selectOnFocus:false,
37481     /**
37482      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37483      */
37484     queryParam: 'query',
37485     /**
37486      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37487      * when mode = 'remote' (defaults to 'Loading...')
37488      */
37489     loadingText: 'Loading...',
37490     /**
37491      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37492      */
37493     resizable: false,
37494     /**
37495      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37496      */
37497     handleHeight : 8,
37498     /**
37499      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37500      * traditional select (defaults to true)
37501      */
37502     editable: true,
37503     /**
37504      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37505      */
37506     allQuery: '',
37507     /**
37508      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37509      */
37510     mode: 'remote',
37511     /**
37512      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37513      * listWidth has a higher value)
37514      */
37515     minListWidth : 70,
37516     /**
37517      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37518      * allow the user to set arbitrary text into the field (defaults to false)
37519      */
37520     forceSelection:false,
37521     /**
37522      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37523      * if typeAhead = true (defaults to 250)
37524      */
37525     typeAheadDelay : 250,
37526     /**
37527      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37528      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37529      */
37530     valueNotFoundText : undefined,
37531     /**
37532      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37533      */
37534     blockFocus : false,
37535     
37536     /**
37537      * @cfg {Boolean} disableClear Disable showing of clear button.
37538      */
37539     disableClear : false,
37540     /**
37541      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37542      */
37543     alwaysQuery : false,
37544     
37545     //private
37546     addicon : false,
37547     editicon: false,
37548     
37549     
37550     // private
37551     onRender : function(ct, position){
37552         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37553         if(this.hiddenName){
37554             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37555                     'before', true);
37556             this.hiddenField.value =
37557                 this.hiddenValue !== undefined ? this.hiddenValue :
37558                 this.value !== undefined ? this.value : '';
37559
37560             // prevent input submission
37561             if (this.hiddenName == this.name) { 
37562                 this.el.dom.removeAttribute('name');
37563             }
37564         }
37565         if(Roo.isGecko){
37566             this.el.dom.setAttribute('autocomplete', 'off');
37567         }
37568
37569         var cls = 'x-combo-list';
37570
37571         this.list = new Roo.Layer({
37572             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37573         });
37574
37575         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37576         this.list.setWidth(lw);
37577         this.list.swallowEvent('mousewheel');
37578         this.assetHeight = 0;
37579
37580         if(this.title){
37581             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37582             this.assetHeight += this.header.getHeight();
37583         }
37584
37585         this.innerList = this.list.createChild({cls:cls+'-inner'});
37586         this.innerList.on('mouseover', this.onViewOver, this);
37587         this.innerList.on('mousemove', this.onViewMove, this);
37588         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37589         
37590         if(this.allowBlank && !this.pageSize && !this.disableClear){
37591             this.footer = this.list.createChild({cls:cls+'-ft'});
37592             this.pageTb = new Roo.Toolbar(this.footer);
37593            
37594         }
37595         if(this.pageSize){
37596             this.footer = this.list.createChild({cls:cls+'-ft'});
37597             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37598                     {pageSize: this.pageSize});
37599             
37600         }
37601         
37602         if (this.pageTb && this.allowBlank && !this.disableClear) {
37603             var _this = this;
37604             this.pageTb.add(new Roo.Toolbar.Fill(), {
37605                 cls: 'x-btn-icon x-btn-clear',
37606                 text: '&#160;',
37607                 handler: function()
37608                 {
37609                     _this.collapse();
37610                     _this.clearValue();
37611                     _this.onSelect(false, -1);
37612                 }
37613             });
37614         }
37615         if (this.footer) {
37616             this.assetHeight += this.footer.getHeight();
37617         }
37618         
37619
37620         if(!this.tpl){
37621             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37622         }
37623
37624         this.view = new Roo.View(this.innerList, this.tpl, {
37625             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37626         });
37627
37628         this.view.on('click', this.onViewClick, this);
37629
37630         this.store.on('beforeload', this.onBeforeLoad, this);
37631         this.store.on('load', this.onLoad, this);
37632         this.store.on('loadexception', this.collapse, this);
37633
37634         if(this.resizable){
37635             this.resizer = new Roo.Resizable(this.list,  {
37636                pinned:true, handles:'se'
37637             });
37638             this.resizer.on('resize', function(r, w, h){
37639                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37640                 this.listWidth = w;
37641                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37642                 this.restrictHeight();
37643             }, this);
37644             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37645         }
37646         if(!this.editable){
37647             this.editable = true;
37648             this.setEditable(false);
37649         }  
37650         
37651         
37652         if (typeof(this.events.add.listeners) != 'undefined') {
37653             
37654             this.addicon = this.wrap.createChild(
37655                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37656        
37657             this.addicon.on('click', function(e) {
37658                 this.fireEvent('add', this);
37659             }, this);
37660         }
37661         if (typeof(this.events.edit.listeners) != 'undefined') {
37662             
37663             this.editicon = this.wrap.createChild(
37664                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37665             if (this.addicon) {
37666                 this.editicon.setStyle('margin-left', '40px');
37667             }
37668             this.editicon.on('click', function(e) {
37669                 
37670                 // we fire even  if inothing is selected..
37671                 this.fireEvent('edit', this, this.lastData );
37672                 
37673             }, this);
37674         }
37675         
37676         
37677         
37678     },
37679
37680     // private
37681     initEvents : function(){
37682         Roo.form.ComboBox.superclass.initEvents.call(this);
37683
37684         this.keyNav = new Roo.KeyNav(this.el, {
37685             "up" : function(e){
37686                 this.inKeyMode = true;
37687                 this.selectPrev();
37688             },
37689
37690             "down" : function(e){
37691                 if(!this.isExpanded()){
37692                     this.onTriggerClick();
37693                 }else{
37694                     this.inKeyMode = true;
37695                     this.selectNext();
37696                 }
37697             },
37698
37699             "enter" : function(e){
37700                 this.onViewClick();
37701                 //return true;
37702             },
37703
37704             "esc" : function(e){
37705                 this.collapse();
37706             },
37707
37708             "tab" : function(e){
37709                 this.onViewClick(false);
37710                 this.fireEvent("specialkey", this, e);
37711                 return true;
37712             },
37713
37714             scope : this,
37715
37716             doRelay : function(foo, bar, hname){
37717                 if(hname == 'down' || this.scope.isExpanded()){
37718                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37719                 }
37720                 return true;
37721             },
37722
37723             forceKeyDown: true
37724         });
37725         this.queryDelay = Math.max(this.queryDelay || 10,
37726                 this.mode == 'local' ? 10 : 250);
37727         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37728         if(this.typeAhead){
37729             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37730         }
37731         if(this.editable !== false){
37732             this.el.on("keyup", this.onKeyUp, this);
37733         }
37734         if(this.forceSelection){
37735             this.on('blur', this.doForce, this);
37736         }
37737     },
37738
37739     onDestroy : function(){
37740         if(this.view){
37741             this.view.setStore(null);
37742             this.view.el.removeAllListeners();
37743             this.view.el.remove();
37744             this.view.purgeListeners();
37745         }
37746         if(this.list){
37747             this.list.destroy();
37748         }
37749         if(this.store){
37750             this.store.un('beforeload', this.onBeforeLoad, this);
37751             this.store.un('load', this.onLoad, this);
37752             this.store.un('loadexception', this.collapse, this);
37753         }
37754         Roo.form.ComboBox.superclass.onDestroy.call(this);
37755     },
37756
37757     // private
37758     fireKey : function(e){
37759         if(e.isNavKeyPress() && !this.list.isVisible()){
37760             this.fireEvent("specialkey", this, e);
37761         }
37762     },
37763
37764     // private
37765     onResize: function(w, h){
37766         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37767         
37768         if(typeof w != 'number'){
37769             // we do not handle it!?!?
37770             return;
37771         }
37772         var tw = this.trigger.getWidth();
37773         tw += this.addicon ? this.addicon.getWidth() : 0;
37774         tw += this.editicon ? this.editicon.getWidth() : 0;
37775         var x = w - tw;
37776         this.el.setWidth( this.adjustWidth('input', x));
37777             
37778         this.trigger.setStyle('left', x+'px');
37779         
37780         if(this.list && this.listWidth === undefined){
37781             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37782             this.list.setWidth(lw);
37783             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37784         }
37785         
37786     
37787         
37788     },
37789
37790     /**
37791      * Allow or prevent the user from directly editing the field text.  If false is passed,
37792      * the user will only be able to select from the items defined in the dropdown list.  This method
37793      * is the runtime equivalent of setting the 'editable' config option at config time.
37794      * @param {Boolean} value True to allow the user to directly edit the field text
37795      */
37796     setEditable : function(value){
37797         if(value == this.editable){
37798             return;
37799         }
37800         this.editable = value;
37801         if(!value){
37802             this.el.dom.setAttribute('readOnly', true);
37803             this.el.on('mousedown', this.onTriggerClick,  this);
37804             this.el.addClass('x-combo-noedit');
37805         }else{
37806             this.el.dom.setAttribute('readOnly', false);
37807             this.el.un('mousedown', this.onTriggerClick,  this);
37808             this.el.removeClass('x-combo-noedit');
37809         }
37810     },
37811
37812     // private
37813     onBeforeLoad : function(){
37814         if(!this.hasFocus){
37815             return;
37816         }
37817         this.innerList.update(this.loadingText ?
37818                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37819         this.restrictHeight();
37820         this.selectedIndex = -1;
37821     },
37822
37823     // private
37824     onLoad : function(){
37825         if(!this.hasFocus){
37826             return;
37827         }
37828         if(this.store.getCount() > 0){
37829             this.expand();
37830             this.restrictHeight();
37831             if(this.lastQuery == this.allQuery){
37832                 if(this.editable){
37833                     this.el.dom.select();
37834                 }
37835                 if(!this.selectByValue(this.value, true)){
37836                     this.select(0, true);
37837                 }
37838             }else{
37839                 this.selectNext();
37840                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37841                     this.taTask.delay(this.typeAheadDelay);
37842                 }
37843             }
37844         }else{
37845             this.onEmptyResults();
37846         }
37847         //this.el.focus();
37848     },
37849
37850     // private
37851     onTypeAhead : function(){
37852         if(this.store.getCount() > 0){
37853             var r = this.store.getAt(0);
37854             var newValue = r.data[this.displayField];
37855             var len = newValue.length;
37856             var selStart = this.getRawValue().length;
37857             if(selStart != len){
37858                 this.setRawValue(newValue);
37859                 this.selectText(selStart, newValue.length);
37860             }
37861         }
37862     },
37863
37864     // private
37865     onSelect : function(record, index){
37866         if(this.fireEvent('beforeselect', this, record, index) !== false){
37867             this.setFromData(index > -1 ? record.data : false);
37868             this.collapse();
37869             this.fireEvent('select', this, record, index);
37870         }
37871     },
37872
37873     /**
37874      * Returns the currently selected field value or empty string if no value is set.
37875      * @return {String} value The selected value
37876      */
37877     getValue : function(){
37878         if(this.valueField){
37879             return typeof this.value != 'undefined' ? this.value : '';
37880         }else{
37881             return Roo.form.ComboBox.superclass.getValue.call(this);
37882         }
37883     },
37884
37885     /**
37886      * Clears any text/value currently set in the field
37887      */
37888     clearValue : function(){
37889         if(this.hiddenField){
37890             this.hiddenField.value = '';
37891         }
37892         this.value = '';
37893         this.setRawValue('');
37894         this.lastSelectionText = '';
37895         this.applyEmptyText();
37896     },
37897
37898     /**
37899      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37900      * will be displayed in the field.  If the value does not match the data value of an existing item,
37901      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37902      * Otherwise the field will be blank (although the value will still be set).
37903      * @param {String} value The value to match
37904      */
37905     setValue : function(v){
37906         var text = v;
37907         if(this.valueField){
37908             var r = this.findRecord(this.valueField, v);
37909             if(r){
37910                 text = r.data[this.displayField];
37911             }else if(this.valueNotFoundText !== undefined){
37912                 text = this.valueNotFoundText;
37913             }
37914         }
37915         this.lastSelectionText = text;
37916         if(this.hiddenField){
37917             this.hiddenField.value = v;
37918         }
37919         Roo.form.ComboBox.superclass.setValue.call(this, text);
37920         this.value = v;
37921     },
37922     /**
37923      * @property {Object} the last set data for the element
37924      */
37925     
37926     lastData : false,
37927     /**
37928      * Sets the value of the field based on a object which is related to the record format for the store.
37929      * @param {Object} value the value to set as. or false on reset?
37930      */
37931     setFromData : function(o){
37932         var dv = ''; // display value
37933         var vv = ''; // value value..
37934         this.lastData = o;
37935         if (this.displayField) {
37936             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37937         } else {
37938             // this is an error condition!!!
37939             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37940         }
37941         
37942         if(this.valueField){
37943             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37944         }
37945         if(this.hiddenField){
37946             this.hiddenField.value = vv;
37947             
37948             this.lastSelectionText = dv;
37949             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37950             this.value = vv;
37951             return;
37952         }
37953         // no hidden field.. - we store the value in 'value', but still display
37954         // display field!!!!
37955         this.lastSelectionText = dv;
37956         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37957         this.value = vv;
37958         
37959         
37960     },
37961     // private
37962     reset : function(){
37963         // overridden so that last data is reset..
37964         this.setValue(this.originalValue);
37965         this.clearInvalid();
37966         this.lastData = false;
37967     },
37968     // private
37969     findRecord : function(prop, value){
37970         var record;
37971         if(this.store.getCount() > 0){
37972             this.store.each(function(r){
37973                 if(r.data[prop] == value){
37974                     record = r;
37975                     return false;
37976                 }
37977             });
37978         }
37979         return record;
37980     },
37981
37982     // private
37983     onViewMove : function(e, t){
37984         this.inKeyMode = false;
37985     },
37986
37987     // private
37988     onViewOver : function(e, t){
37989         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37990             return;
37991         }
37992         var item = this.view.findItemFromChild(t);
37993         if(item){
37994             var index = this.view.indexOf(item);
37995             this.select(index, false);
37996         }
37997     },
37998
37999     // private
38000     onViewClick : function(doFocus)
38001     {
38002         var index = this.view.getSelectedIndexes()[0];
38003         var r = this.store.getAt(index);
38004         if(r){
38005             this.onSelect(r, index);
38006         }
38007         if(doFocus !== false && !this.blockFocus){
38008             this.el.focus();
38009         }
38010     },
38011
38012     // private
38013     restrictHeight : function(){
38014         this.innerList.dom.style.height = '';
38015         var inner = this.innerList.dom;
38016         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38017         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38018         this.list.beginUpdate();
38019         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38020         this.list.alignTo(this.el, this.listAlign);
38021         this.list.endUpdate();
38022     },
38023
38024     // private
38025     onEmptyResults : function(){
38026         this.collapse();
38027     },
38028
38029     /**
38030      * Returns true if the dropdown list is expanded, else false.
38031      */
38032     isExpanded : function(){
38033         return this.list.isVisible();
38034     },
38035
38036     /**
38037      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38038      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38039      * @param {String} value The data value of the item to select
38040      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38041      * selected item if it is not currently in view (defaults to true)
38042      * @return {Boolean} True if the value matched an item in the list, else false
38043      */
38044     selectByValue : function(v, scrollIntoView){
38045         if(v !== undefined && v !== null){
38046             var r = this.findRecord(this.valueField || this.displayField, v);
38047             if(r){
38048                 this.select(this.store.indexOf(r), scrollIntoView);
38049                 return true;
38050             }
38051         }
38052         return false;
38053     },
38054
38055     /**
38056      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38058      * @param {Number} index The zero-based index of the list item to select
38059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38060      * selected item if it is not currently in view (defaults to true)
38061      */
38062     select : function(index, scrollIntoView){
38063         this.selectedIndex = index;
38064         this.view.select(index);
38065         if(scrollIntoView !== false){
38066             var el = this.view.getNode(index);
38067             if(el){
38068                 this.innerList.scrollChildIntoView(el, false);
38069             }
38070         }
38071     },
38072
38073     // private
38074     selectNext : function(){
38075         var ct = this.store.getCount();
38076         if(ct > 0){
38077             if(this.selectedIndex == -1){
38078                 this.select(0);
38079             }else if(this.selectedIndex < ct-1){
38080                 this.select(this.selectedIndex+1);
38081             }
38082         }
38083     },
38084
38085     // private
38086     selectPrev : function(){
38087         var ct = this.store.getCount();
38088         if(ct > 0){
38089             if(this.selectedIndex == -1){
38090                 this.select(0);
38091             }else if(this.selectedIndex != 0){
38092                 this.select(this.selectedIndex-1);
38093             }
38094         }
38095     },
38096
38097     // private
38098     onKeyUp : function(e){
38099         if(this.editable !== false && !e.isSpecialKey()){
38100             this.lastKey = e.getKey();
38101             this.dqTask.delay(this.queryDelay);
38102         }
38103     },
38104
38105     // private
38106     validateBlur : function(){
38107         return !this.list || !this.list.isVisible();   
38108     },
38109
38110     // private
38111     initQuery : function(){
38112         this.doQuery(this.getRawValue());
38113     },
38114
38115     // private
38116     doForce : function(){
38117         if(this.el.dom.value.length > 0){
38118             this.el.dom.value =
38119                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38120             this.applyEmptyText();
38121         }
38122     },
38123
38124     /**
38125      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38126      * query allowing the query action to be canceled if needed.
38127      * @param {String} query The SQL query to execute
38128      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38129      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38130      * saved in the current store (defaults to false)
38131      */
38132     doQuery : function(q, forceAll){
38133         if(q === undefined || q === null){
38134             q = '';
38135         }
38136         var qe = {
38137             query: q,
38138             forceAll: forceAll,
38139             combo: this,
38140             cancel:false
38141         };
38142         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38143             return false;
38144         }
38145         q = qe.query;
38146         forceAll = qe.forceAll;
38147         if(forceAll === true || (q.length >= this.minChars)){
38148             if(this.lastQuery != q || this.alwaysQuery){
38149                 this.lastQuery = q;
38150                 if(this.mode == 'local'){
38151                     this.selectedIndex = -1;
38152                     if(forceAll){
38153                         this.store.clearFilter();
38154                     }else{
38155                         this.store.filter(this.displayField, q);
38156                     }
38157                     this.onLoad();
38158                 }else{
38159                     this.store.baseParams[this.queryParam] = q;
38160                     this.store.load({
38161                         params: this.getParams(q)
38162                     });
38163                     this.expand();
38164                 }
38165             }else{
38166                 this.selectedIndex = -1;
38167                 this.onLoad();   
38168             }
38169         }
38170     },
38171
38172     // private
38173     getParams : function(q){
38174         var p = {};
38175         //p[this.queryParam] = q;
38176         if(this.pageSize){
38177             p.start = 0;
38178             p.limit = this.pageSize;
38179         }
38180         return p;
38181     },
38182
38183     /**
38184      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38185      */
38186     collapse : function(){
38187         if(!this.isExpanded()){
38188             return;
38189         }
38190         this.list.hide();
38191         Roo.get(document).un('mousedown', this.collapseIf, this);
38192         Roo.get(document).un('mousewheel', this.collapseIf, this);
38193         if (!this.editable) {
38194             Roo.get(document).un('keydown', this.listKeyPress, this);
38195         }
38196         this.fireEvent('collapse', this);
38197     },
38198
38199     // private
38200     collapseIf : function(e){
38201         if(!e.within(this.wrap) && !e.within(this.list)){
38202             this.collapse();
38203         }
38204     },
38205
38206     /**
38207      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38208      */
38209     expand : function(){
38210         if(this.isExpanded() || !this.hasFocus){
38211             return;
38212         }
38213         this.list.alignTo(this.el, this.listAlign);
38214         this.list.show();
38215         Roo.get(document).on('mousedown', this.collapseIf, this);
38216         Roo.get(document).on('mousewheel', this.collapseIf, this);
38217         if (!this.editable) {
38218             Roo.get(document).on('keydown', this.listKeyPress, this);
38219         }
38220         
38221         this.fireEvent('expand', this);
38222     },
38223
38224     // private
38225     // Implements the default empty TriggerField.onTriggerClick function
38226     onTriggerClick : function(){
38227         if(this.disabled){
38228             return;
38229         }
38230         if(this.isExpanded()){
38231             this.collapse();
38232             if (!this.blockFocus) {
38233                 this.el.focus();
38234             }
38235             
38236         }else {
38237             this.hasFocus = true;
38238             if(this.triggerAction == 'all') {
38239                 this.doQuery(this.allQuery, true);
38240             } else {
38241                 this.doQuery(this.getRawValue());
38242             }
38243             if (!this.blockFocus) {
38244                 this.el.focus();
38245             }
38246         }
38247     },
38248     listKeyPress : function(e)
38249     {
38250         //Roo.log('listkeypress');
38251         // scroll to first matching element based on key pres..
38252         if (e.isSpecialKey()) {
38253             return false;
38254         }
38255         var k = String.fromCharCode(e.getKey()).toUpperCase();
38256         //Roo.log(k);
38257         var match  = false;
38258         var csel = this.view.getSelectedNodes();
38259         var cselitem = false;
38260         if (csel.length) {
38261             var ix = this.view.indexOf(csel[0]);
38262             cselitem  = this.store.getAt(ix);
38263             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38264                 cselitem = false;
38265             }
38266             
38267         }
38268         
38269         this.store.each(function(v) { 
38270             if (cselitem) {
38271                 // start at existing selection.
38272                 if (cselitem.id == v.id) {
38273                     cselitem = false;
38274                 }
38275                 return;
38276             }
38277                 
38278             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38279                 match = this.store.indexOf(v);
38280                 return false;
38281             }
38282         }, this);
38283         
38284         if (match === false) {
38285             return true; // no more action?
38286         }
38287         // scroll to?
38288         this.view.select(match);
38289         var sn = Roo.get(this.view.getSelectedNodes()[0])
38290         sn.scrollIntoView(sn.dom.parentNode, false);
38291     }
38292
38293     /** 
38294     * @cfg {Boolean} grow 
38295     * @hide 
38296     */
38297     /** 
38298     * @cfg {Number} growMin 
38299     * @hide 
38300     */
38301     /** 
38302     * @cfg {Number} growMax 
38303     * @hide 
38304     */
38305     /**
38306      * @hide
38307      * @method autoSize
38308      */
38309 });/*
38310  * Based on:
38311  * Ext JS Library 1.1.1
38312  * Copyright(c) 2006-2007, Ext JS, LLC.
38313  *
38314  * Originally Released Under LGPL - original licence link has changed is not relivant.
38315  *
38316  * Fork - LGPL
38317  * <script type="text/javascript">
38318  */
38319 /**
38320  * @class Roo.form.Checkbox
38321  * @extends Roo.form.Field
38322  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38323  * @constructor
38324  * Creates a new Checkbox
38325  * @param {Object} config Configuration options
38326  */
38327 Roo.form.Checkbox = function(config){
38328     Roo.form.Checkbox.superclass.constructor.call(this, config);
38329     this.addEvents({
38330         /**
38331          * @event check
38332          * Fires when the checkbox is checked or unchecked.
38333              * @param {Roo.form.Checkbox} this This checkbox
38334              * @param {Boolean} checked The new checked value
38335              */
38336         check : true
38337     });
38338 };
38339
38340 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38341     /**
38342      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38343      */
38344     focusClass : undefined,
38345     /**
38346      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38347      */
38348     fieldClass: "x-form-field",
38349     /**
38350      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38351      */
38352     checked: false,
38353     /**
38354      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38355      * {tag: "input", type: "checkbox", autocomplete: "off"})
38356      */
38357     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38358     /**
38359      * @cfg {String} boxLabel The text that appears beside the checkbox
38360      */
38361     boxLabel : "",
38362     /**
38363      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38364      */  
38365     inputValue : '1',
38366     /**
38367      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38368      */
38369      valueOff: '0', // value when not checked..
38370
38371     actionMode : 'viewEl', 
38372     //
38373     // private
38374     itemCls : 'x-menu-check-item x-form-item',
38375     groupClass : 'x-menu-group-item',
38376     inputType : 'hidden',
38377     
38378     
38379     inSetChecked: false, // check that we are not calling self...
38380     
38381     inputElement: false, // real input element?
38382     basedOn: false, // ????
38383     
38384     isFormField: true, // not sure where this is needed!!!!
38385
38386     onResize : function(){
38387         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38388         if(!this.boxLabel){
38389             this.el.alignTo(this.wrap, 'c-c');
38390         }
38391     },
38392
38393     initEvents : function(){
38394         Roo.form.Checkbox.superclass.initEvents.call(this);
38395         this.el.on("click", this.onClick,  this);
38396         this.el.on("change", this.onClick,  this);
38397     },
38398
38399
38400     getResizeEl : function(){
38401         return this.wrap;
38402     },
38403
38404     getPositionEl : function(){
38405         return this.wrap;
38406     },
38407
38408     // private
38409     onRender : function(ct, position){
38410         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38411         /*
38412         if(this.inputValue !== undefined){
38413             this.el.dom.value = this.inputValue;
38414         }
38415         */
38416         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38417         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38418         var viewEl = this.wrap.createChild({ 
38419             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38420         this.viewEl = viewEl;   
38421         this.wrap.on('click', this.onClick,  this); 
38422         
38423         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38424         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38425         
38426         
38427         
38428         if(this.boxLabel){
38429             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38430         //    viewEl.on('click', this.onClick,  this); 
38431         }
38432         //if(this.checked){
38433             this.setChecked(this.checked);
38434         //}else{
38435             //this.checked = this.el.dom;
38436         //}
38437
38438     },
38439
38440     // private
38441     initValue : Roo.emptyFn,
38442
38443     /**
38444      * Returns the checked state of the checkbox.
38445      * @return {Boolean} True if checked, else false
38446      */
38447     getValue : function(){
38448         if(this.el){
38449             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38450         }
38451         return this.valueOff;
38452         
38453     },
38454
38455         // private
38456     onClick : function(){ 
38457         this.setChecked(!this.checked);
38458
38459         //if(this.el.dom.checked != this.checked){
38460         //    this.setValue(this.el.dom.checked);
38461        // }
38462     },
38463
38464     /**
38465      * Sets the checked state of the checkbox.
38466      * On is always based on a string comparison between inputValue and the param.
38467      * @param {Boolean/String} value - the value to set 
38468      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38469      */
38470     setValue : function(v,suppressEvent){
38471         
38472         
38473         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38474         //if(this.el && this.el.dom){
38475         //    this.el.dom.checked = this.checked;
38476         //    this.el.dom.defaultChecked = this.checked;
38477         //}
38478         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38479         //this.fireEvent("check", this, this.checked);
38480     },
38481     // private..
38482     setChecked : function(state,suppressEvent)
38483     {
38484         if (this.inSetChecked) {
38485             this.checked = state;
38486             return;
38487         }
38488         
38489     
38490         if(this.wrap){
38491             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38492         }
38493         this.checked = state;
38494         if(suppressEvent !== true){
38495             this.fireEvent('check', this, state);
38496         }
38497         this.inSetChecked = true;
38498         this.el.dom.value = state ? this.inputValue : this.valueOff;
38499         this.inSetChecked = false;
38500         
38501     },
38502     // handle setting of hidden value by some other method!!?!?
38503     setFromHidden: function()
38504     {
38505         if(!this.el){
38506             return;
38507         }
38508         //console.log("SET FROM HIDDEN");
38509         //alert('setFrom hidden');
38510         this.setValue(this.el.dom.value);
38511     },
38512     
38513     onDestroy : function()
38514     {
38515         if(this.viewEl){
38516             Roo.get(this.viewEl).remove();
38517         }
38518          
38519         Roo.form.Checkbox.superclass.onDestroy.call(this);
38520     }
38521
38522 });/*
38523  * Based on:
38524  * Ext JS Library 1.1.1
38525  * Copyright(c) 2006-2007, Ext JS, LLC.
38526  *
38527  * Originally Released Under LGPL - original licence link has changed is not relivant.
38528  *
38529  * Fork - LGPL
38530  * <script type="text/javascript">
38531  */
38532  
38533 /**
38534  * @class Roo.form.Radio
38535  * @extends Roo.form.Checkbox
38536  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38537  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38538  * @constructor
38539  * Creates a new Radio
38540  * @param {Object} config Configuration options
38541  */
38542 Roo.form.Radio = function(){
38543     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38544 };
38545 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38546     inputType: 'radio',
38547
38548     /**
38549      * If this radio is part of a group, it will return the selected value
38550      * @return {String}
38551      */
38552     getGroupValue : function(){
38553         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38554     }
38555 });//<script type="text/javascript">
38556
38557 /*
38558  * Ext JS Library 1.1.1
38559  * Copyright(c) 2006-2007, Ext JS, LLC.
38560  * licensing@extjs.com
38561  * 
38562  * http://www.extjs.com/license
38563  */
38564  
38565  /*
38566   * 
38567   * Known bugs:
38568   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38569   * - IE ? - no idea how much works there.
38570   * 
38571   * 
38572   * 
38573   */
38574  
38575
38576 /**
38577  * @class Ext.form.HtmlEditor
38578  * @extends Ext.form.Field
38579  * Provides a lightweight HTML Editor component.
38580  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38581  * 
38582  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38583  * supported by this editor.</b><br/><br/>
38584  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38585  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38586  */
38587 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38588       /**
38589      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38590      */
38591     toolbars : false,
38592     /**
38593      * @cfg {String} createLinkText The default text for the create link prompt
38594      */
38595     createLinkText : 'Please enter the URL for the link:',
38596     /**
38597      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38598      */
38599     defaultLinkValue : 'http:/'+'/',
38600    
38601      /**
38602      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38603      *                        Roo.resizable.
38604      */
38605     resizable : false,
38606      /**
38607      * @cfg {Number} height (in pixels)
38608      */   
38609     height: 300,
38610    /**
38611      * @cfg {Number} width (in pixels)
38612      */   
38613     width: 500,
38614     
38615     /**
38616      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38617      * 
38618      */
38619     stylesheets: false,
38620     
38621     // id of frame..
38622     frameId: false,
38623     
38624     // private properties
38625     validationEvent : false,
38626     deferHeight: true,
38627     initialized : false,
38628     activated : false,
38629     sourceEditMode : false,
38630     onFocus : Roo.emptyFn,
38631     iframePad:3,
38632     hideMode:'offsets',
38633     
38634     defaultAutoCreate : { // modified by initCompnoent..
38635         tag: "textarea",
38636         style:"width:500px;height:300px;",
38637         autocomplete: "off"
38638     },
38639
38640     // private
38641     initComponent : function(){
38642         this.addEvents({
38643             /**
38644              * @event initialize
38645              * Fires when the editor is fully initialized (including the iframe)
38646              * @param {HtmlEditor} this
38647              */
38648             initialize: true,
38649             /**
38650              * @event activate
38651              * Fires when the editor is first receives the focus. Any insertion must wait
38652              * until after this event.
38653              * @param {HtmlEditor} this
38654              */
38655             activate: true,
38656              /**
38657              * @event beforesync
38658              * Fires before the textarea is updated with content from the editor iframe. Return false
38659              * to cancel the sync.
38660              * @param {HtmlEditor} this
38661              * @param {String} html
38662              */
38663             beforesync: true,
38664              /**
38665              * @event beforepush
38666              * Fires before the iframe editor is updated with content from the textarea. Return false
38667              * to cancel the push.
38668              * @param {HtmlEditor} this
38669              * @param {String} html
38670              */
38671             beforepush: true,
38672              /**
38673              * @event sync
38674              * Fires when the textarea is updated with content from the editor iframe.
38675              * @param {HtmlEditor} this
38676              * @param {String} html
38677              */
38678             sync: true,
38679              /**
38680              * @event push
38681              * Fires when the iframe editor is updated with content from the textarea.
38682              * @param {HtmlEditor} this
38683              * @param {String} html
38684              */
38685             push: true,
38686              /**
38687              * @event editmodechange
38688              * Fires when the editor switches edit modes
38689              * @param {HtmlEditor} this
38690              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38691              */
38692             editmodechange: true,
38693             /**
38694              * @event editorevent
38695              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38696              * @param {HtmlEditor} this
38697              */
38698             editorevent: true
38699         });
38700         this.defaultAutoCreate =  {
38701             tag: "textarea",
38702             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38703             autocomplete: "off"
38704         };
38705     },
38706
38707     /**
38708      * Protected method that will not generally be called directly. It
38709      * is called when the editor creates its toolbar. Override this method if you need to
38710      * add custom toolbar buttons.
38711      * @param {HtmlEditor} editor
38712      */
38713     createToolbar : function(editor){
38714         if (!editor.toolbars || !editor.toolbars.length) {
38715             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38716         }
38717         
38718         for (var i =0 ; i < editor.toolbars.length;i++) {
38719             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38720             editor.toolbars[i].init(editor);
38721         }
38722          
38723         
38724     },
38725
38726     /**
38727      * Protected method that will not generally be called directly. It
38728      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38729      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38730      */
38731     getDocMarkup : function(){
38732         // body styles..
38733         var st = '';
38734         if (this.stylesheets === false) {
38735             
38736             Roo.get(document.head).select('style').each(function(node) {
38737                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38738             });
38739             
38740             Roo.get(document.head).select('link').each(function(node) { 
38741                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38742             });
38743             
38744         } else if (!this.stylesheets.length) {
38745                 // simple..
38746                 st = '<style type="text/css">' +
38747                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38748                    '</style>';
38749         } else {
38750             Roo.each(this.stylesheets, function(s) {
38751                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38752             });
38753             
38754         }
38755         
38756         return '<html><head>' + st  +
38757             //<style type="text/css">' +
38758             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38759             //'</style>' +
38760             ' </head><body></body></html>';
38761     },
38762
38763     // private
38764     onRender : function(ct, position)
38765     {
38766         var _t = this;
38767         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38768         this.el.dom.style.border = '0 none';
38769         this.el.dom.setAttribute('tabIndex', -1);
38770         this.el.addClass('x-hidden');
38771         if(Roo.isIE){ // fix IE 1px bogus margin
38772             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38773         }
38774         this.wrap = this.el.wrap({
38775             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38776         });
38777         
38778         if (this.resizable) {
38779             this.resizeEl = new Roo.Resizable(this.wrap, {
38780                 pinned : true,
38781                 wrap: true,
38782                 dynamic : true,
38783                 minHeight : this.height,
38784                 height: this.height,
38785                 handles : this.resizable,
38786                 width: this.width,
38787                 listeners : {
38788                     resize : function(r, w, h) {
38789                         _t.onResize(w,h); // -something
38790                     }
38791                 }
38792             });
38793             
38794         }
38795
38796         this.frameId = Roo.id();
38797         
38798         this.createToolbar(this);
38799         
38800       
38801         
38802         var iframe = this.wrap.createChild({
38803             tag: 'iframe',
38804             id: this.frameId,
38805             name: this.frameId,
38806             frameBorder : 'no',
38807             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38808         }, this.el
38809         );
38810         
38811        // console.log(iframe);
38812         //this.wrap.dom.appendChild(iframe);
38813
38814         this.iframe = iframe.dom;
38815
38816          this.assignDocWin();
38817         
38818         this.doc.designMode = 'on';
38819        
38820         this.doc.open();
38821         this.doc.write(this.getDocMarkup());
38822         this.doc.close();
38823
38824         
38825         var task = { // must defer to wait for browser to be ready
38826             run : function(){
38827                 //console.log("run task?" + this.doc.readyState);
38828                 this.assignDocWin();
38829                 if(this.doc.body || this.doc.readyState == 'complete'){
38830                     try {
38831                         this.doc.designMode="on";
38832                     } catch (e) {
38833                         return;
38834                     }
38835                     Roo.TaskMgr.stop(task);
38836                     this.initEditor.defer(10, this);
38837                 }
38838             },
38839             interval : 10,
38840             duration:10000,
38841             scope: this
38842         };
38843         Roo.TaskMgr.start(task);
38844
38845         if(!this.width){
38846             this.setSize(this.wrap.getSize());
38847         }
38848         if (this.resizeEl) {
38849             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38850             // should trigger onReize..
38851         }
38852     },
38853
38854     // private
38855     onResize : function(w, h)
38856     {
38857         //Roo.log('resize: ' +w + ',' + h );
38858         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38859         if(this.el && this.iframe){
38860             if(typeof w == 'number'){
38861                 var aw = w - this.wrap.getFrameWidth('lr');
38862                 this.el.setWidth(this.adjustWidth('textarea', aw));
38863                 this.iframe.style.width = aw + 'px';
38864             }
38865             if(typeof h == 'number'){
38866                 var tbh = 0;
38867                 for (var i =0; i < this.toolbars.length;i++) {
38868                     // fixme - ask toolbars for heights?
38869                     tbh += this.toolbars[i].tb.el.getHeight();
38870                     if (this.toolbars[i].footer) {
38871                         tbh += this.toolbars[i].footer.el.getHeight();
38872                     }
38873                 }
38874                 
38875                 
38876                 
38877                 
38878                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38879                 ah -= 5; // knock a few pixes off for look..
38880                 this.el.setHeight(this.adjustWidth('textarea', ah));
38881                 this.iframe.style.height = ah + 'px';
38882                 if(this.doc){
38883                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38884                 }
38885             }
38886         }
38887     },
38888
38889     /**
38890      * Toggles the editor between standard and source edit mode.
38891      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38892      */
38893     toggleSourceEdit : function(sourceEditMode){
38894         
38895         this.sourceEditMode = sourceEditMode === true;
38896         
38897         if(this.sourceEditMode){
38898           
38899             this.syncValue();
38900             this.iframe.className = 'x-hidden';
38901             this.el.removeClass('x-hidden');
38902             this.el.dom.removeAttribute('tabIndex');
38903             this.el.focus();
38904         }else{
38905              
38906             this.pushValue();
38907             this.iframe.className = '';
38908             this.el.addClass('x-hidden');
38909             this.el.dom.setAttribute('tabIndex', -1);
38910             this.deferFocus();
38911         }
38912         this.setSize(this.wrap.getSize());
38913         this.fireEvent('editmodechange', this, this.sourceEditMode);
38914     },
38915
38916     // private used internally
38917     createLink : function(){
38918         var url = prompt(this.createLinkText, this.defaultLinkValue);
38919         if(url && url != 'http:/'+'/'){
38920             this.relayCmd('createlink', url);
38921         }
38922     },
38923
38924     // private (for BoxComponent)
38925     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38926
38927     // private (for BoxComponent)
38928     getResizeEl : function(){
38929         return this.wrap;
38930     },
38931
38932     // private (for BoxComponent)
38933     getPositionEl : function(){
38934         return this.wrap;
38935     },
38936
38937     // private
38938     initEvents : function(){
38939         this.originalValue = this.getValue();
38940     },
38941
38942     /**
38943      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38944      * @method
38945      */
38946     markInvalid : Roo.emptyFn,
38947     /**
38948      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38949      * @method
38950      */
38951     clearInvalid : Roo.emptyFn,
38952
38953     setValue : function(v){
38954         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38955         this.pushValue();
38956     },
38957
38958     /**
38959      * Protected method that will not generally be called directly. If you need/want
38960      * custom HTML cleanup, this is the method you should override.
38961      * @param {String} html The HTML to be cleaned
38962      * return {String} The cleaned HTML
38963      */
38964     cleanHtml : function(html){
38965         html = String(html);
38966         if(html.length > 5){
38967             if(Roo.isSafari){ // strip safari nonsense
38968                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38969             }
38970         }
38971         if(html == '&nbsp;'){
38972             html = '';
38973         }
38974         return html;
38975     },
38976
38977     /**
38978      * Protected method that will not generally be called directly. Syncs the contents
38979      * of the editor iframe with the textarea.
38980      */
38981     syncValue : function(){
38982         if(this.initialized){
38983             var bd = (this.doc.body || this.doc.documentElement);
38984             this.cleanUpPaste();
38985             var html = bd.innerHTML;
38986             if(Roo.isSafari){
38987                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38988                 var m = bs.match(/text-align:(.*?);/i);
38989                 if(m && m[1]){
38990                     html = '<div style="'+m[0]+'">' + html + '</div>';
38991                 }
38992             }
38993             html = this.cleanHtml(html);
38994             if(this.fireEvent('beforesync', this, html) !== false){
38995                 this.el.dom.value = html;
38996                 this.fireEvent('sync', this, html);
38997             }
38998         }
38999     },
39000
39001     /**
39002      * Protected method that will not generally be called directly. Pushes the value of the textarea
39003      * into the iframe editor.
39004      */
39005     pushValue : function(){
39006         if(this.initialized){
39007             var v = this.el.dom.value;
39008             if(v.length < 1){
39009                 v = '&#160;';
39010             }
39011             
39012             if(this.fireEvent('beforepush', this, v) !== false){
39013                 var d = (this.doc.body || this.doc.documentElement);
39014                 d.innerHTML = v;
39015                 this.cleanUpPaste();
39016                 this.el.dom.value = d.innerHTML;
39017                 this.fireEvent('push', this, v);
39018             }
39019         }
39020     },
39021
39022     // private
39023     deferFocus : function(){
39024         this.focus.defer(10, this);
39025     },
39026
39027     // doc'ed in Field
39028     focus : function(){
39029         if(this.win && !this.sourceEditMode){
39030             this.win.focus();
39031         }else{
39032             this.el.focus();
39033         }
39034     },
39035     
39036     assignDocWin: function()
39037     {
39038         var iframe = this.iframe;
39039         
39040          if(Roo.isIE){
39041             this.doc = iframe.contentWindow.document;
39042             this.win = iframe.contentWindow;
39043         } else {
39044             if (!Roo.get(this.frameId)) {
39045                 return;
39046             }
39047             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39048             this.win = Roo.get(this.frameId).dom.contentWindow;
39049         }
39050     },
39051     
39052     // private
39053     initEditor : function(){
39054         //console.log("INIT EDITOR");
39055         this.assignDocWin();
39056         
39057         
39058         
39059         this.doc.designMode="on";
39060         this.doc.open();
39061         this.doc.write(this.getDocMarkup());
39062         this.doc.close();
39063         
39064         var dbody = (this.doc.body || this.doc.documentElement);
39065         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39066         // this copies styles from the containing element into thsi one..
39067         // not sure why we need all of this..
39068         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39069         ss['background-attachment'] = 'fixed'; // w3c
39070         dbody.bgProperties = 'fixed'; // ie
39071         Roo.DomHelper.applyStyles(dbody, ss);
39072         Roo.EventManager.on(this.doc, {
39073             //'mousedown': this.onEditorEvent,
39074             'mouseup': this.onEditorEvent,
39075             'dblclick': this.onEditorEvent,
39076             'click': this.onEditorEvent,
39077             'keyup': this.onEditorEvent,
39078             buffer:100,
39079             scope: this
39080         });
39081         if(Roo.isGecko){
39082             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39083         }
39084         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39085             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39086         }
39087         this.initialized = true;
39088
39089         this.fireEvent('initialize', this);
39090         this.pushValue();
39091     },
39092
39093     // private
39094     onDestroy : function(){
39095         
39096         
39097         
39098         if(this.rendered){
39099             
39100             for (var i =0; i < this.toolbars.length;i++) {
39101                 // fixme - ask toolbars for heights?
39102                 this.toolbars[i].onDestroy();
39103             }
39104             
39105             this.wrap.dom.innerHTML = '';
39106             this.wrap.remove();
39107         }
39108     },
39109
39110     // private
39111     onFirstFocus : function(){
39112         
39113         this.assignDocWin();
39114         
39115         
39116         this.activated = true;
39117         for (var i =0; i < this.toolbars.length;i++) {
39118             this.toolbars[i].onFirstFocus();
39119         }
39120        
39121         if(Roo.isGecko){ // prevent silly gecko errors
39122             this.win.focus();
39123             var s = this.win.getSelection();
39124             if(!s.focusNode || s.focusNode.nodeType != 3){
39125                 var r = s.getRangeAt(0);
39126                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39127                 r.collapse(true);
39128                 this.deferFocus();
39129             }
39130             try{
39131                 this.execCmd('useCSS', true);
39132                 this.execCmd('styleWithCSS', false);
39133             }catch(e){}
39134         }
39135         this.fireEvent('activate', this);
39136     },
39137
39138     // private
39139     adjustFont: function(btn){
39140         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39141         //if(Roo.isSafari){ // safari
39142         //    adjust *= 2;
39143        // }
39144         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39145         if(Roo.isSafari){ // safari
39146             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39147             v =  (v < 10) ? 10 : v;
39148             v =  (v > 48) ? 48 : v;
39149             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39150             
39151         }
39152         
39153         
39154         v = Math.max(1, v+adjust);
39155         
39156         this.execCmd('FontSize', v  );
39157     },
39158
39159     onEditorEvent : function(e){
39160         this.fireEvent('editorevent', this, e);
39161       //  this.updateToolbar();
39162         this.syncValue();
39163     },
39164
39165     insertTag : function(tg)
39166     {
39167         // could be a bit smarter... -> wrap the current selected tRoo..
39168         
39169         this.execCmd("formatblock",   tg);
39170         
39171     },
39172     
39173     insertText : function(txt)
39174     {
39175         
39176         
39177         range = this.createRange();
39178         range.deleteContents();
39179                //alert(Sender.getAttribute('label'));
39180                
39181         range.insertNode(this.doc.createTextNode(txt));
39182     } ,
39183     
39184     // private
39185     relayBtnCmd : function(btn){
39186         this.relayCmd(btn.cmd);
39187     },
39188
39189     /**
39190      * Executes a Midas editor command on the editor document and performs necessary focus and
39191      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39192      * @param {String} cmd The Midas command
39193      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39194      */
39195     relayCmd : function(cmd, value){
39196         this.win.focus();
39197         this.execCmd(cmd, value);
39198         this.fireEvent('editorevent', this);
39199         //this.updateToolbar();
39200         this.deferFocus();
39201     },
39202
39203     /**
39204      * Executes a Midas editor command directly on the editor document.
39205      * For visual commands, you should use {@link #relayCmd} instead.
39206      * <b>This should only be called after the editor is initialized.</b>
39207      * @param {String} cmd The Midas command
39208      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39209      */
39210     execCmd : function(cmd, value){
39211         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39212         this.syncValue();
39213     },
39214
39215    
39216     /**
39217      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39218      * to insert tRoo.
39219      * @param {String} text
39220      */
39221     insertAtCursor : function(text){
39222         if(!this.activated){
39223             return;
39224         }
39225         if(Roo.isIE){
39226             this.win.focus();
39227             var r = this.doc.selection.createRange();
39228             if(r){
39229                 r.collapse(true);
39230                 r.pasteHTML(text);
39231                 this.syncValue();
39232                 this.deferFocus();
39233             }
39234         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39235             this.win.focus();
39236             this.execCmd('InsertHTML', text);
39237             this.deferFocus();
39238         }
39239     },
39240  // private
39241     mozKeyPress : function(e){
39242         if(e.ctrlKey){
39243             var c = e.getCharCode(), cmd;
39244           
39245             if(c > 0){
39246                 c = String.fromCharCode(c).toLowerCase();
39247                 switch(c){
39248                     case 'b':
39249                         cmd = 'bold';
39250                     break;
39251                     case 'i':
39252                         cmd = 'italic';
39253                     break;
39254                     case 'u':
39255                         cmd = 'underline';
39256                     case 'v':
39257                         this.cleanUpPaste.defer(100, this);
39258                         return;
39259                     break;
39260                 }
39261                 if(cmd){
39262                     this.win.focus();
39263                     this.execCmd(cmd);
39264                     this.deferFocus();
39265                     e.preventDefault();
39266                 }
39267                 
39268             }
39269         }
39270     },
39271
39272     // private
39273     fixKeys : function(){ // load time branching for fastest keydown performance
39274         if(Roo.isIE){
39275             return function(e){
39276                 var k = e.getKey(), r;
39277                 if(k == e.TAB){
39278                     e.stopEvent();
39279                     r = this.doc.selection.createRange();
39280                     if(r){
39281                         r.collapse(true);
39282                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39283                         this.deferFocus();
39284                     }
39285                     return;
39286                 }
39287                 
39288                 if(k == e.ENTER){
39289                     r = this.doc.selection.createRange();
39290                     if(r){
39291                         var target = r.parentElement();
39292                         if(!target || target.tagName.toLowerCase() != 'li'){
39293                             e.stopEvent();
39294                             r.pasteHTML('<br />');
39295                             r.collapse(false);
39296                             r.select();
39297                         }
39298                     }
39299                 }
39300                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39301                     this.cleanUpPaste.defer(100, this);
39302                     return;
39303                 }
39304                 
39305                 
39306             };
39307         }else if(Roo.isOpera){
39308             return function(e){
39309                 var k = e.getKey();
39310                 if(k == e.TAB){
39311                     e.stopEvent();
39312                     this.win.focus();
39313                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39314                     this.deferFocus();
39315                 }
39316                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39317                     this.cleanUpPaste.defer(100, this);
39318                     return;
39319                 }
39320                 
39321             };
39322         }else if(Roo.isSafari){
39323             return function(e){
39324                 var k = e.getKey();
39325                 
39326                 if(k == e.TAB){
39327                     e.stopEvent();
39328                     this.execCmd('InsertText','\t');
39329                     this.deferFocus();
39330                     return;
39331                 }
39332                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39333                     this.cleanUpPaste.defer(100, this);
39334                     return;
39335                 }
39336                 
39337              };
39338         }
39339     }(),
39340     
39341     getAllAncestors: function()
39342     {
39343         var p = this.getSelectedNode();
39344         var a = [];
39345         if (!p) {
39346             a.push(p); // push blank onto stack..
39347             p = this.getParentElement();
39348         }
39349         
39350         
39351         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39352             a.push(p);
39353             p = p.parentNode;
39354         }
39355         a.push(this.doc.body);
39356         return a;
39357     },
39358     lastSel : false,
39359     lastSelNode : false,
39360     
39361     
39362     getSelection : function() 
39363     {
39364         this.assignDocWin();
39365         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39366     },
39367     
39368     getSelectedNode: function() 
39369     {
39370         // this may only work on Gecko!!!
39371         
39372         // should we cache this!!!!
39373         
39374         
39375         
39376          
39377         var range = this.createRange(this.getSelection()).cloneRange();
39378         
39379         if (Roo.isIE) {
39380             var parent = range.parentElement();
39381             while (true) {
39382                 var testRange = range.duplicate();
39383                 testRange.moveToElementText(parent);
39384                 if (testRange.inRange(range)) {
39385                     break;
39386                 }
39387                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39388                     break;
39389                 }
39390                 parent = parent.parentElement;
39391             }
39392             return parent;
39393         }
39394         
39395         // is ancestor a text element.
39396         var ac =  range.commonAncestorContainer;
39397         if (ac.nodeType == 3) {
39398             ac = ac.parentNode;
39399         }
39400         
39401         var ar = ac.childNodes;
39402          
39403         var nodes = [];
39404         var other_nodes = [];
39405         var has_other_nodes = false;
39406         for (var i=0;i<ar.length;i++) {
39407             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39408                 continue;
39409             }
39410             // fullly contained node.
39411             
39412             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39413                 nodes.push(ar[i]);
39414                 continue;
39415             }
39416             
39417             // probably selected..
39418             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39419                 other_nodes.push(ar[i]);
39420                 continue;
39421             }
39422             // outer..
39423             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39424                 continue;
39425             }
39426             
39427             
39428             has_other_nodes = true;
39429         }
39430         if (!nodes.length && other_nodes.length) {
39431             nodes= other_nodes;
39432         }
39433         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39434             return false;
39435         }
39436         
39437         return nodes[0];
39438     },
39439     createRange: function(sel)
39440     {
39441         // this has strange effects when using with 
39442         // top toolbar - not sure if it's a great idea.
39443         //this.editor.contentWindow.focus();
39444         if (typeof sel != "undefined") {
39445             try {
39446                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39447             } catch(e) {
39448                 return this.doc.createRange();
39449             }
39450         } else {
39451             return this.doc.createRange();
39452         }
39453     },
39454     getParentElement: function()
39455     {
39456         
39457         this.assignDocWin();
39458         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39459         
39460         var range = this.createRange(sel);
39461          
39462         try {
39463             var p = range.commonAncestorContainer;
39464             while (p.nodeType == 3) { // text node
39465                 p = p.parentNode;
39466             }
39467             return p;
39468         } catch (e) {
39469             return null;
39470         }
39471     
39472     },
39473     /***
39474      *
39475      * Range intersection.. the hard stuff...
39476      *  '-1' = before
39477      *  '0' = hits..
39478      *  '1' = after.
39479      *         [ -- selected range --- ]
39480      *   [fail]                        [fail]
39481      *
39482      *    basically..
39483      *      if end is before start or  hits it. fail.
39484      *      if start is after end or hits it fail.
39485      *
39486      *   if either hits (but other is outside. - then it's not 
39487      *   
39488      *    
39489      **/
39490     
39491     
39492     // @see http://www.thismuchiknow.co.uk/?p=64.
39493     rangeIntersectsNode : function(range, node)
39494     {
39495         var nodeRange = node.ownerDocument.createRange();
39496         try {
39497             nodeRange.selectNode(node);
39498         } catch (e) {
39499             nodeRange.selectNodeContents(node);
39500         }
39501     
39502         var rangeStartRange = range.cloneRange();
39503         rangeStartRange.collapse(true);
39504     
39505         var rangeEndRange = range.cloneRange();
39506         rangeEndRange.collapse(false);
39507     
39508         var nodeStartRange = nodeRange.cloneRange();
39509         nodeStartRange.collapse(true);
39510     
39511         var nodeEndRange = nodeRange.cloneRange();
39512         nodeEndRange.collapse(false);
39513     
39514         return rangeStartRange.compareBoundaryPoints(
39515                  Range.START_TO_START, nodeEndRange) == -1 &&
39516                rangeEndRange.compareBoundaryPoints(
39517                  Range.START_TO_START, nodeStartRange) == 1;
39518         
39519          
39520     },
39521     rangeCompareNode : function(range, node)
39522     {
39523         var nodeRange = node.ownerDocument.createRange();
39524         try {
39525             nodeRange.selectNode(node);
39526         } catch (e) {
39527             nodeRange.selectNodeContents(node);
39528         }
39529         
39530         
39531         range.collapse(true);
39532     
39533         nodeRange.collapse(true);
39534      
39535         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39536         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39537          
39538         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39539         
39540         var nodeIsBefore   =  ss == 1;
39541         var nodeIsAfter    = ee == -1;
39542         
39543         if (nodeIsBefore && nodeIsAfter)
39544             return 0; // outer
39545         if (!nodeIsBefore && nodeIsAfter)
39546             return 1; //right trailed.
39547         
39548         if (nodeIsBefore && !nodeIsAfter)
39549             return 2;  // left trailed.
39550         // fully contined.
39551         return 3;
39552     },
39553
39554     // private? - in a new class?
39555     cleanUpPaste :  function()
39556     {
39557         // cleans up the whole document..
39558       //  console.log('cleanuppaste');
39559         this.cleanUpChildren(this.doc.body);
39560         
39561         
39562     },
39563     cleanUpChildren : function (n)
39564     {
39565         if (!n.childNodes.length) {
39566             return;
39567         }
39568         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39569            this.cleanUpChild(n.childNodes[i]);
39570         }
39571     },
39572     
39573     
39574         
39575     
39576     cleanUpChild : function (node)
39577     {
39578         //console.log(node);
39579         if (node.nodeName == "#text") {
39580             // clean up silly Windows -- stuff?
39581             return; 
39582         }
39583         if (node.nodeName == "#comment") {
39584             node.parentNode.removeChild(node);
39585             // clean up silly Windows -- stuff?
39586             return; 
39587         }
39588         
39589         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39590             // remove node.
39591             node.parentNode.removeChild(node);
39592             return;
39593             
39594         }
39595         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39596             this.cleanUpChildren(node);
39597             // inserts everything just before this node...
39598             while (node.childNodes.length) {
39599                 var cn = node.childNodes[0];
39600                 node.removeChild(cn);
39601                 node.parentNode.insertBefore(cn, node);
39602             }
39603             node.parentNode.removeChild(node);
39604             return;
39605         }
39606         
39607         if (!node.attributes || !node.attributes.length) {
39608             this.cleanUpChildren(node);
39609             return;
39610         }
39611         
39612         function cleanAttr(n,v)
39613         {
39614             
39615             if (v.match(/^\./) || v.match(/^\//)) {
39616                 return;
39617             }
39618             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39619                 return;
39620             }
39621             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39622             node.removeAttribute(n);
39623             
39624         }
39625         
39626         function cleanStyle(n,v)
39627         {
39628             if (v.match(/expression/)) { //XSS?? should we even bother..
39629                 node.removeAttribute(n);
39630                 return;
39631             }
39632             
39633             
39634             var parts = v.split(/;/);
39635             Roo.each(parts, function(p) {
39636                 p = p.replace(/\s+/g,'');
39637                 if (!p.length) {
39638                     return true;
39639                 }
39640                 var l = p.split(':').shift().replace(/\s+/g,'');
39641                 
39642                 // only allow 'c whitelisted system attributes'
39643                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39644                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39645                     node.removeAttribute(n);
39646                     return false;
39647                 }
39648                 return true;
39649             });
39650             
39651             
39652         }
39653         
39654         
39655         for (var i = node.attributes.length-1; i > -1 ; i--) {
39656             var a = node.attributes[i];
39657             //console.log(a);
39658             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39659                 node.removeAttribute(a.name);
39660                 return;
39661             }
39662             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39663                 cleanAttr(a.name,a.value); // fixme..
39664                 return;
39665             }
39666             if (a.name == 'style') {
39667                 cleanStyle(a.name,a.value);
39668             }
39669             /// clean up MS crap..
39670             if (a.name == 'class') {
39671                 if (a.value.match(/^Mso/)) {
39672                     node.className = '';
39673                 }
39674             }
39675             
39676             // style cleanup!?
39677             // class cleanup?
39678             
39679         }
39680         
39681         
39682         this.cleanUpChildren(node);
39683         
39684         
39685     }
39686     
39687     
39688     // hide stuff that is not compatible
39689     /**
39690      * @event blur
39691      * @hide
39692      */
39693     /**
39694      * @event change
39695      * @hide
39696      */
39697     /**
39698      * @event focus
39699      * @hide
39700      */
39701     /**
39702      * @event specialkey
39703      * @hide
39704      */
39705     /**
39706      * @cfg {String} fieldClass @hide
39707      */
39708     /**
39709      * @cfg {String} focusClass @hide
39710      */
39711     /**
39712      * @cfg {String} autoCreate @hide
39713      */
39714     /**
39715      * @cfg {String} inputType @hide
39716      */
39717     /**
39718      * @cfg {String} invalidClass @hide
39719      */
39720     /**
39721      * @cfg {String} invalidText @hide
39722      */
39723     /**
39724      * @cfg {String} msgFx @hide
39725      */
39726     /**
39727      * @cfg {String} validateOnBlur @hide
39728      */
39729 });
39730
39731 Roo.form.HtmlEditor.white = [
39732         'area', 'br', 'img', 'input', 'hr', 'wbr',
39733         
39734        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39735        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39736        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39737        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39738        'table',   'ul',         'xmp', 
39739        
39740        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39741       'thead',   'tr', 
39742      
39743       'dir', 'menu', 'ol', 'ul', 'dl',
39744        
39745       'embed',  'object'
39746 ];
39747
39748
39749 Roo.form.HtmlEditor.black = [
39750     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39751         'applet', // 
39752         'base',   'basefont', 'bgsound', 'blink',  'body', 
39753         'frame',  'frameset', 'head',    'html',   'ilayer', 
39754         'iframe', 'layer',  'link',     'meta',    'object',   
39755         'script', 'style' ,'title',  'xml' // clean later..
39756 ];
39757 Roo.form.HtmlEditor.clean = [
39758     'script', 'style', 'title', 'xml'
39759 ];
39760 Roo.form.HtmlEditor.remove = [
39761     'font'
39762 ];
39763 // attributes..
39764
39765 Roo.form.HtmlEditor.ablack = [
39766     'on'
39767 ];
39768     
39769 Roo.form.HtmlEditor.aclean = [ 
39770     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39771 ];
39772
39773 // protocols..
39774 Roo.form.HtmlEditor.pwhite= [
39775         'http',  'https',  'mailto'
39776 ];
39777
39778 // white listed style attributes.
39779 Roo.form.HtmlEditor.cwhite= [
39780         'text-align',
39781         'font-size'
39782 ];
39783
39784 // <script type="text/javascript">
39785 /*
39786  * Based on
39787  * Ext JS Library 1.1.1
39788  * Copyright(c) 2006-2007, Ext JS, LLC.
39789  *  
39790  
39791  */
39792
39793 /**
39794  * @class Roo.form.HtmlEditorToolbar1
39795  * Basic Toolbar
39796  * 
39797  * Usage:
39798  *
39799  new Roo.form.HtmlEditor({
39800     ....
39801     toolbars : [
39802         new Roo.form.HtmlEditorToolbar1({
39803             disable : { fonts: 1 , format: 1, ..., ... , ...],
39804             btns : [ .... ]
39805         })
39806     }
39807      
39808  * 
39809  * @cfg {Object} disable List of elements to disable..
39810  * @cfg {Array} btns List of additional buttons.
39811  * 
39812  * 
39813  * NEEDS Extra CSS? 
39814  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39815  */
39816  
39817 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39818 {
39819     
39820     Roo.apply(this, config);
39821     
39822     // default disabled, based on 'good practice'..
39823     this.disable = this.disable || {};
39824     Roo.applyIf(this.disable, {
39825         fontSize : true,
39826         colors : true,
39827         specialElements : true
39828     });
39829     
39830     
39831     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39832     // dont call parent... till later.
39833 }
39834
39835 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39836     
39837     tb: false,
39838     
39839     rendered: false,
39840     
39841     editor : false,
39842     /**
39843      * @cfg {Object} disable  List of toolbar elements to disable
39844          
39845      */
39846     disable : false,
39847       /**
39848      * @cfg {Array} fontFamilies An array of available font families
39849      */
39850     fontFamilies : [
39851         'Arial',
39852         'Courier New',
39853         'Tahoma',
39854         'Times New Roman',
39855         'Verdana'
39856     ],
39857     
39858     specialChars : [
39859            "&#169;",
39860           "&#174;",     
39861           "&#8482;",    
39862           "&#163;" ,    
39863          // "&#8212;",    
39864           "&#8230;",    
39865           "&#247;" ,    
39866         //  "&#225;" ,     ?? a acute?
39867            "&#8364;"    , //Euro
39868        //   "&#8220;"    ,
39869         //  "&#8221;"    ,
39870         //  "&#8226;"    ,
39871           "&#176;"  //   , // degrees
39872
39873          // "&#233;"     , // e ecute
39874          // "&#250;"     , // u ecute?
39875     ],
39876     
39877     specialElements : [
39878         {
39879             text: "Insert Table",
39880             xtype: 'MenuItem',
39881             xns : Roo.Menu,
39882             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39883                 
39884         },
39885         {    
39886             text: "Insert Image",
39887             xtype: 'MenuItem',
39888             xns : Roo.Menu,
39889             ihtml : '<img src="about:blank"/>'
39890             
39891         }
39892         
39893          
39894     ],
39895     
39896     
39897     inputElements : [ 
39898             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39899             "input:submit", "input:button", "select", "textarea", "label" ],
39900     formats : [
39901         ["p"] ,  
39902         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39903         ["pre"],[ "code"], 
39904         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39905     ],
39906      /**
39907      * @cfg {String} defaultFont default font to use.
39908      */
39909     defaultFont: 'tahoma',
39910    
39911     fontSelect : false,
39912     
39913     
39914     formatCombo : false,
39915     
39916     init : function(editor)
39917     {
39918         this.editor = editor;
39919         
39920         
39921         var fid = editor.frameId;
39922         var etb = this;
39923         function btn(id, toggle, handler){
39924             var xid = fid + '-'+ id ;
39925             return {
39926                 id : xid,
39927                 cmd : id,
39928                 cls : 'x-btn-icon x-edit-'+id,
39929                 enableToggle:toggle !== false,
39930                 scope: editor, // was editor...
39931                 handler:handler||editor.relayBtnCmd,
39932                 clickEvent:'mousedown',
39933                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39934                 tabIndex:-1
39935             };
39936         }
39937         
39938         
39939         
39940         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39941         this.tb = tb;
39942          // stop form submits
39943         tb.el.on('click', function(e){
39944             e.preventDefault(); // what does this do?
39945         });
39946
39947         if(!this.disable.font && !Roo.isSafari){
39948             /* why no safari for fonts
39949             editor.fontSelect = tb.el.createChild({
39950                 tag:'select',
39951                 tabIndex: -1,
39952                 cls:'x-font-select',
39953                 html: editor.createFontOptions()
39954             });
39955             editor.fontSelect.on('change', function(){
39956                 var font = editor.fontSelect.dom.value;
39957                 editor.relayCmd('fontname', font);
39958                 editor.deferFocus();
39959             }, editor);
39960             tb.add(
39961                 editor.fontSelect.dom,
39962                 '-'
39963             );
39964             */
39965         };
39966         if(!this.disable.formats){
39967             this.formatCombo = new Roo.form.ComboBox({
39968                 store: new Roo.data.SimpleStore({
39969                     id : 'tag',
39970                     fields: ['tag'],
39971                     data : this.formats // from states.js
39972                 }),
39973                 blockFocus : true,
39974                 //autoCreate : {tag: "div",  size: "20"},
39975                 displayField:'tag',
39976                 typeAhead: false,
39977                 mode: 'local',
39978                 editable : false,
39979                 triggerAction: 'all',
39980                 emptyText:'Add tag',
39981                 selectOnFocus:true,
39982                 width:135,
39983                 listeners : {
39984                     'select': function(c, r, i) {
39985                         editor.insertTag(r.get('tag'));
39986                         editor.focus();
39987                     }
39988                 }
39989
39990             });
39991             tb.addField(this.formatCombo);
39992             
39993         }
39994         
39995         if(!this.disable.format){
39996             tb.add(
39997                 btn('bold'),
39998                 btn('italic'),
39999                 btn('underline')
40000             );
40001         };
40002         if(!this.disable.fontSize){
40003             tb.add(
40004                 '-',
40005                 
40006                 
40007                 btn('increasefontsize', false, editor.adjustFont),
40008                 btn('decreasefontsize', false, editor.adjustFont)
40009             );
40010         };
40011         
40012         
40013         if(!this.disable.colors){
40014             tb.add(
40015                 '-', {
40016                     id:editor.frameId +'-forecolor',
40017                     cls:'x-btn-icon x-edit-forecolor',
40018                     clickEvent:'mousedown',
40019                     tooltip: this.buttonTips['forecolor'] || undefined,
40020                     tabIndex:-1,
40021                     menu : new Roo.menu.ColorMenu({
40022                         allowReselect: true,
40023                         focus: Roo.emptyFn,
40024                         value:'000000',
40025                         plain:true,
40026                         selectHandler: function(cp, color){
40027                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40028                             editor.deferFocus();
40029                         },
40030                         scope: editor,
40031                         clickEvent:'mousedown'
40032                     })
40033                 }, {
40034                     id:editor.frameId +'backcolor',
40035                     cls:'x-btn-icon x-edit-backcolor',
40036                     clickEvent:'mousedown',
40037                     tooltip: this.buttonTips['backcolor'] || undefined,
40038                     tabIndex:-1,
40039                     menu : new Roo.menu.ColorMenu({
40040                         focus: Roo.emptyFn,
40041                         value:'FFFFFF',
40042                         plain:true,
40043                         allowReselect: true,
40044                         selectHandler: function(cp, color){
40045                             if(Roo.isGecko){
40046                                 editor.execCmd('useCSS', false);
40047                                 editor.execCmd('hilitecolor', color);
40048                                 editor.execCmd('useCSS', true);
40049                                 editor.deferFocus();
40050                             }else{
40051                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40052                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40053                                 editor.deferFocus();
40054                             }
40055                         },
40056                         scope:editor,
40057                         clickEvent:'mousedown'
40058                     })
40059                 }
40060             );
40061         };
40062         // now add all the items...
40063         
40064
40065         if(!this.disable.alignments){
40066             tb.add(
40067                 '-',
40068                 btn('justifyleft'),
40069                 btn('justifycenter'),
40070                 btn('justifyright')
40071             );
40072         };
40073
40074         //if(!Roo.isSafari){
40075             if(!this.disable.links){
40076                 tb.add(
40077                     '-',
40078                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40079                 );
40080             };
40081
40082             if(!this.disable.lists){
40083                 tb.add(
40084                     '-',
40085                     btn('insertorderedlist'),
40086                     btn('insertunorderedlist')
40087                 );
40088             }
40089             if(!this.disable.sourceEdit){
40090                 tb.add(
40091                     '-',
40092                     btn('sourceedit', true, function(btn){
40093                         this.toggleSourceEdit(btn.pressed);
40094                     })
40095                 );
40096             }
40097         //}
40098         
40099         var smenu = { };
40100         // special menu.. - needs to be tidied up..
40101         if (!this.disable.special) {
40102             smenu = {
40103                 text: "&#169;",
40104                 cls: 'x-edit-none',
40105                 
40106                 menu : {
40107                     items : []
40108                 }
40109             };
40110             for (var i =0; i < this.specialChars.length; i++) {
40111                 smenu.menu.items.push({
40112                     
40113                     html: this.specialChars[i],
40114                     handler: function(a,b) {
40115                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40116                         
40117                     },
40118                     tabIndex:-1
40119                 });
40120             }
40121             
40122             
40123             tb.add(smenu);
40124             
40125             
40126         }
40127          
40128         if (!this.disable.specialElements) {
40129             var semenu = {
40130                 text: "Other;",
40131                 cls: 'x-edit-none',
40132                 menu : {
40133                     items : []
40134                 }
40135             };
40136             for (var i =0; i < this.specialElements.length; i++) {
40137                 semenu.menu.items.push(
40138                     Roo.apply({ 
40139                         handler: function(a,b) {
40140                             editor.insertAtCursor(this.ihtml);
40141                         }
40142                     }, this.specialElements[i])
40143                 );
40144                     
40145             }
40146             
40147             tb.add(semenu);
40148             
40149             
40150         }
40151          
40152         
40153         if (this.btns) {
40154             for(var i =0; i< this.btns.length;i++) {
40155                 var b = this.btns[i];
40156                 b.cls =  'x-edit-none';
40157                 b.scope = editor;
40158                 tb.add(b);
40159             }
40160         
40161         }
40162         
40163         
40164         
40165         // disable everything...
40166         
40167         this.tb.items.each(function(item){
40168            if(item.id != editor.frameId+ '-sourceedit'){
40169                 item.disable();
40170             }
40171         });
40172         this.rendered = true;
40173         
40174         // the all the btns;
40175         editor.on('editorevent', this.updateToolbar, this);
40176         // other toolbars need to implement this..
40177         //editor.on('editmodechange', this.updateToolbar, this);
40178     },
40179     
40180     
40181     
40182     /**
40183      * Protected method that will not generally be called directly. It triggers
40184      * a toolbar update by reading the markup state of the current selection in the editor.
40185      */
40186     updateToolbar: function(){
40187
40188         if(!this.editor.activated){
40189             this.editor.onFirstFocus();
40190             return;
40191         }
40192
40193         var btns = this.tb.items.map, 
40194             doc = this.editor.doc,
40195             frameId = this.editor.frameId;
40196
40197         if(!this.disable.font && !Roo.isSafari){
40198             /*
40199             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40200             if(name != this.fontSelect.dom.value){
40201                 this.fontSelect.dom.value = name;
40202             }
40203             */
40204         }
40205         if(!this.disable.format){
40206             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40207             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40208             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40209         }
40210         if(!this.disable.alignments){
40211             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40212             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40213             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40214         }
40215         if(!Roo.isSafari && !this.disable.lists){
40216             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40217             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40218         }
40219         
40220         var ans = this.editor.getAllAncestors();
40221         if (this.formatCombo) {
40222             
40223             
40224             var store = this.formatCombo.store;
40225             this.formatCombo.setValue("");
40226             for (var i =0; i < ans.length;i++) {
40227                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40228                     // select it..
40229                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40230                     break;
40231                 }
40232             }
40233         }
40234         
40235         
40236         
40237         // hides menus... - so this cant be on a menu...
40238         Roo.menu.MenuMgr.hideAll();
40239
40240         //this.editorsyncValue();
40241     },
40242    
40243     
40244     createFontOptions : function(){
40245         var buf = [], fs = this.fontFamilies, ff, lc;
40246         for(var i = 0, len = fs.length; i< len; i++){
40247             ff = fs[i];
40248             lc = ff.toLowerCase();
40249             buf.push(
40250                 '<option value="',lc,'" style="font-family:',ff,';"',
40251                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40252                     ff,
40253                 '</option>'
40254             );
40255         }
40256         return buf.join('');
40257     },
40258     
40259     toggleSourceEdit : function(sourceEditMode){
40260         if(sourceEditMode === undefined){
40261             sourceEditMode = !this.sourceEditMode;
40262         }
40263         this.sourceEditMode = sourceEditMode === true;
40264         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40265         // just toggle the button?
40266         if(btn.pressed !== this.editor.sourceEditMode){
40267             btn.toggle(this.editor.sourceEditMode);
40268             return;
40269         }
40270         
40271         if(this.sourceEditMode){
40272             this.tb.items.each(function(item){
40273                 if(item.cmd != 'sourceedit'){
40274                     item.disable();
40275                 }
40276             });
40277           
40278         }else{
40279             if(this.initialized){
40280                 this.tb.items.each(function(item){
40281                     item.enable();
40282                 });
40283             }
40284             
40285         }
40286         // tell the editor that it's been pressed..
40287         this.editor.toggleSourceEdit(sourceEditMode);
40288        
40289     },
40290      /**
40291      * Object collection of toolbar tooltips for the buttons in the editor. The key
40292      * is the command id associated with that button and the value is a valid QuickTips object.
40293      * For example:
40294 <pre><code>
40295 {
40296     bold : {
40297         title: 'Bold (Ctrl+B)',
40298         text: 'Make the selected text bold.',
40299         cls: 'x-html-editor-tip'
40300     },
40301     italic : {
40302         title: 'Italic (Ctrl+I)',
40303         text: 'Make the selected text italic.',
40304         cls: 'x-html-editor-tip'
40305     },
40306     ...
40307 </code></pre>
40308     * @type Object
40309      */
40310     buttonTips : {
40311         bold : {
40312             title: 'Bold (Ctrl+B)',
40313             text: 'Make the selected text bold.',
40314             cls: 'x-html-editor-tip'
40315         },
40316         italic : {
40317             title: 'Italic (Ctrl+I)',
40318             text: 'Make the selected text italic.',
40319             cls: 'x-html-editor-tip'
40320         },
40321         underline : {
40322             title: 'Underline (Ctrl+U)',
40323             text: 'Underline the selected text.',
40324             cls: 'x-html-editor-tip'
40325         },
40326         increasefontsize : {
40327             title: 'Grow Text',
40328             text: 'Increase the font size.',
40329             cls: 'x-html-editor-tip'
40330         },
40331         decreasefontsize : {
40332             title: 'Shrink Text',
40333             text: 'Decrease the font size.',
40334             cls: 'x-html-editor-tip'
40335         },
40336         backcolor : {
40337             title: 'Text Highlight Color',
40338             text: 'Change the background color of the selected text.',
40339             cls: 'x-html-editor-tip'
40340         },
40341         forecolor : {
40342             title: 'Font Color',
40343             text: 'Change the color of the selected text.',
40344             cls: 'x-html-editor-tip'
40345         },
40346         justifyleft : {
40347             title: 'Align Text Left',
40348             text: 'Align text to the left.',
40349             cls: 'x-html-editor-tip'
40350         },
40351         justifycenter : {
40352             title: 'Center Text',
40353             text: 'Center text in the editor.',
40354             cls: 'x-html-editor-tip'
40355         },
40356         justifyright : {
40357             title: 'Align Text Right',
40358             text: 'Align text to the right.',
40359             cls: 'x-html-editor-tip'
40360         },
40361         insertunorderedlist : {
40362             title: 'Bullet List',
40363             text: 'Start a bulleted list.',
40364             cls: 'x-html-editor-tip'
40365         },
40366         insertorderedlist : {
40367             title: 'Numbered List',
40368             text: 'Start a numbered list.',
40369             cls: 'x-html-editor-tip'
40370         },
40371         createlink : {
40372             title: 'Hyperlink',
40373             text: 'Make the selected text a hyperlink.',
40374             cls: 'x-html-editor-tip'
40375         },
40376         sourceedit : {
40377             title: 'Source Edit',
40378             text: 'Switch to source editing mode.',
40379             cls: 'x-html-editor-tip'
40380         }
40381     },
40382     // private
40383     onDestroy : function(){
40384         if(this.rendered){
40385             
40386             this.tb.items.each(function(item){
40387                 if(item.menu){
40388                     item.menu.removeAll();
40389                     if(item.menu.el){
40390                         item.menu.el.destroy();
40391                     }
40392                 }
40393                 item.destroy();
40394             });
40395              
40396         }
40397     },
40398     onFirstFocus: function() {
40399         this.tb.items.each(function(item){
40400            item.enable();
40401         });
40402     }
40403 });
40404
40405
40406
40407
40408 // <script type="text/javascript">
40409 /*
40410  * Based on
40411  * Ext JS Library 1.1.1
40412  * Copyright(c) 2006-2007, Ext JS, LLC.
40413  *  
40414  
40415  */
40416
40417  
40418 /**
40419  * @class Roo.form.HtmlEditor.ToolbarContext
40420  * Context Toolbar
40421  * 
40422  * Usage:
40423  *
40424  new Roo.form.HtmlEditor({
40425     ....
40426     toolbars : [
40427         { xtype: 'ToolbarStandard', styles : {} }
40428         { xtype: 'ToolbarContext', disable : {} }
40429     ]
40430 })
40431
40432      
40433  * 
40434  * @config : {Object} disable List of elements to disable.. (not done yet.)
40435  * @config : {Object} styles  Map of styles available.
40436  * 
40437  */
40438
40439 Roo.form.HtmlEditor.ToolbarContext = function(config)
40440 {
40441     
40442     Roo.apply(this, config);
40443     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40444     // dont call parent... till later.
40445     this.styles = this.styles || {};
40446 }
40447 Roo.form.HtmlEditor.ToolbarContext.types = {
40448     'IMG' : {
40449         width : {
40450             title: "Width",
40451             width: 40
40452         },
40453         height:  {
40454             title: "Height",
40455             width: 40
40456         },
40457         align: {
40458             title: "Align",
40459             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40460             width : 80
40461             
40462         },
40463         border: {
40464             title: "Border",
40465             width: 40
40466         },
40467         alt: {
40468             title: "Alt",
40469             width: 120
40470         },
40471         src : {
40472             title: "Src",
40473             width: 220
40474         }
40475         
40476     },
40477     'A' : {
40478         name : {
40479             title: "Name",
40480             width: 50
40481         },
40482         href:  {
40483             title: "Href",
40484             width: 220
40485         } // border?
40486         
40487     },
40488     'TABLE' : {
40489         rows : {
40490             title: "Rows",
40491             width: 20
40492         },
40493         cols : {
40494             title: "Cols",
40495             width: 20
40496         },
40497         width : {
40498             title: "Width",
40499             width: 40
40500         },
40501         height : {
40502             title: "Height",
40503             width: 40
40504         },
40505         border : {
40506             title: "Border",
40507             width: 20
40508         }
40509     },
40510     'TD' : {
40511         width : {
40512             title: "Width",
40513             width: 40
40514         },
40515         height : {
40516             title: "Height",
40517             width: 40
40518         },   
40519         align: {
40520             title: "Align",
40521             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40522             width: 80
40523         },
40524         valign: {
40525             title: "Valign",
40526             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40527             width: 80
40528         },
40529         colspan: {
40530             title: "Colspan",
40531             width: 20
40532             
40533         }
40534     },
40535     'INPUT' : {
40536         name : {
40537             title: "name",
40538             width: 120
40539         },
40540         value : {
40541             title: "Value",
40542             width: 120
40543         },
40544         width : {
40545             title: "Width",
40546             width: 40
40547         }
40548     },
40549     'LABEL' : {
40550         'for' : {
40551             title: "For",
40552             width: 120
40553         }
40554     },
40555     'TEXTAREA' : {
40556           name : {
40557             title: "name",
40558             width: 120
40559         },
40560         rows : {
40561             title: "Rows",
40562             width: 20
40563         },
40564         cols : {
40565             title: "Cols",
40566             width: 20
40567         }
40568     },
40569     'SELECT' : {
40570         name : {
40571             title: "name",
40572             width: 120
40573         },
40574         selectoptions : {
40575             title: "Options",
40576             width: 200
40577         }
40578     },
40579     
40580     // should we really allow this??
40581     // should this just be 
40582     'BODY' : {
40583         title : {
40584             title: "title",
40585             width: 200,
40586             disabled : true
40587         }
40588     },
40589     '*' : {
40590         // empty..
40591     }
40592 };
40593
40594
40595
40596 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40597     
40598     tb: false,
40599     
40600     rendered: false,
40601     
40602     editor : false,
40603     /**
40604      * @cfg {Object} disable  List of toolbar elements to disable
40605          
40606      */
40607     disable : false,
40608     /**
40609      * @cfg {Object} styles List of styles 
40610      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40611      *
40612      * These must be defined in the page, so they get rendered correctly..
40613      * .headline { }
40614      * TD.underline { }
40615      * 
40616      */
40617     styles : false,
40618     
40619     
40620     
40621     toolbars : false,
40622     
40623     init : function(editor)
40624     {
40625         this.editor = editor;
40626         
40627         
40628         var fid = editor.frameId;
40629         var etb = this;
40630         function btn(id, toggle, handler){
40631             var xid = fid + '-'+ id ;
40632             return {
40633                 id : xid,
40634                 cmd : id,
40635                 cls : 'x-btn-icon x-edit-'+id,
40636                 enableToggle:toggle !== false,
40637                 scope: editor, // was editor...
40638                 handler:handler||editor.relayBtnCmd,
40639                 clickEvent:'mousedown',
40640                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40641                 tabIndex:-1
40642             };
40643         }
40644         // create a new element.
40645         var wdiv = editor.wrap.createChild({
40646                 tag: 'div'
40647             }, editor.wrap.dom.firstChild.nextSibling, true);
40648         
40649         // can we do this more than once??
40650         
40651          // stop form submits
40652       
40653  
40654         // disable everything...
40655         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40656         this.toolbars = {};
40657            
40658         for (var i in  ty) {
40659           
40660             this.toolbars[i] = this.buildToolbar(ty[i],i);
40661         }
40662         this.tb = this.toolbars.BODY;
40663         this.tb.el.show();
40664         this.buildFooter();
40665         this.footer.show();
40666          
40667         this.rendered = true;
40668         
40669         // the all the btns;
40670         editor.on('editorevent', this.updateToolbar, this);
40671         // other toolbars need to implement this..
40672         //editor.on('editmodechange', this.updateToolbar, this);
40673     },
40674     
40675     
40676     
40677     /**
40678      * Protected method that will not generally be called directly. It triggers
40679      * a toolbar update by reading the markup state of the current selection in the editor.
40680      */
40681     updateToolbar: function(ignore_a,ignore_b,sel){
40682
40683         
40684         if(!this.editor.activated){
40685              this.editor.onFirstFocus();
40686             return;
40687         }
40688         var updateFooter = sel ? false : true;
40689         
40690         
40691         var ans = this.editor.getAllAncestors();
40692         
40693         // pick
40694         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40695         
40696         if (!sel) { 
40697             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40698             sel = sel ? sel : this.editor.doc.body;
40699             sel = sel.tagName.length ? sel : this.editor.doc.body;
40700             
40701         }
40702         // pick a menu that exists..
40703         var tn = sel.tagName.toUpperCase();
40704         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40705         
40706         tn = sel.tagName.toUpperCase();
40707         
40708         var lastSel = this.tb.selectedNode
40709         
40710         this.tb.selectedNode = sel;
40711         
40712         // if current menu does not match..
40713         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40714                 
40715             this.tb.el.hide();
40716             ///console.log("show: " + tn);
40717             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40718             this.tb.el.show();
40719             // update name
40720             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40721             
40722             
40723             // update attributes
40724             if (this.tb.fields) {
40725                 this.tb.fields.each(function(e) {
40726                    e.setValue(sel.getAttribute(e.name));
40727                 });
40728             }
40729             
40730             // update styles
40731             var st = this.tb.fields.item(0);
40732             st.store.removeAll();
40733             var cn = sel.className.split(/\s+/);
40734             
40735             var avs = [];
40736             if (this.styles['*']) {
40737                 
40738                 Roo.each(this.styles['*'], function(v) {
40739                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40740                 });
40741             }
40742             if (this.styles[tn]) { 
40743                 Roo.each(this.styles[tn], function(v) {
40744                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40745                 });
40746             }
40747             
40748             st.store.loadData(avs);
40749             st.collapse();
40750             st.setValue(cn);
40751             
40752             // flag our selected Node.
40753             this.tb.selectedNode = sel;
40754            
40755            
40756             Roo.menu.MenuMgr.hideAll();
40757
40758         }
40759         
40760         if (!updateFooter) {
40761             return;
40762         }
40763         // update the footer
40764         //
40765         var html = '';
40766         
40767         this.footerEls = ans.reverse();
40768         Roo.each(this.footerEls, function(a,i) {
40769             if (!a) { return; }
40770             html += html.length ? ' &gt; '  :  '';
40771             
40772             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40773             
40774         });
40775        
40776         // 
40777         var sz = this.footDisp.up('td').getSize();
40778         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40779         this.footDisp.dom.style.marginLeft = '5px';
40780         
40781         this.footDisp.dom.style.overflow = 'hidden';
40782         
40783         this.footDisp.dom.innerHTML = html;
40784             
40785         //this.editorsyncValue();
40786     },
40787    
40788        
40789     // private
40790     onDestroy : function(){
40791         if(this.rendered){
40792             
40793             this.tb.items.each(function(item){
40794                 if(item.menu){
40795                     item.menu.removeAll();
40796                     if(item.menu.el){
40797                         item.menu.el.destroy();
40798                     }
40799                 }
40800                 item.destroy();
40801             });
40802              
40803         }
40804     },
40805     onFirstFocus: function() {
40806         // need to do this for all the toolbars..
40807         this.tb.items.each(function(item){
40808            item.enable();
40809         });
40810     },
40811     buildToolbar: function(tlist, nm)
40812     {
40813         var editor = this.editor;
40814          // create a new element.
40815         var wdiv = editor.wrap.createChild({
40816                 tag: 'div'
40817             }, editor.wrap.dom.firstChild.nextSibling, true);
40818         
40819        
40820         var tb = new Roo.Toolbar(wdiv);
40821         // add the name..
40822         
40823         tb.add(nm+ ":&nbsp;");
40824         
40825         // styles...
40826         if (this.styles) {
40827             
40828             // this needs a multi-select checkbox...
40829             tb.addField( new Roo.form.ComboBox({
40830                 store: new Roo.data.SimpleStore({
40831                     id : 'val',
40832                     fields: ['val', 'selected'],
40833                     data : [] 
40834                 }),
40835                 name : 'className',
40836                 displayField:'val',
40837                 typeAhead: false,
40838                 mode: 'local',
40839                 editable : false,
40840                 triggerAction: 'all',
40841                 emptyText:'Select Style',
40842                 selectOnFocus:true,
40843                 width: 130,
40844                 listeners : {
40845                     'select': function(c, r, i) {
40846                         // initial support only for on class per el..
40847                         tb.selectedNode.className =  r ? r.get('val') : '';
40848                     }
40849                 }
40850     
40851             }));
40852         }
40853             
40854         
40855         
40856         for (var i in tlist) {
40857             
40858             var item = tlist[i];
40859             tb.add(item.title + ":&nbsp;");
40860             
40861             
40862             
40863             
40864             if (item.opts) {
40865                 // opts == pulldown..
40866                 tb.addField( new Roo.form.ComboBox({
40867                     store: new Roo.data.SimpleStore({
40868                         id : 'val',
40869                         fields: ['val'],
40870                         data : item.opts  
40871                     }),
40872                     name : i,
40873                     displayField:'val',
40874                     typeAhead: false,
40875                     mode: 'local',
40876                     editable : false,
40877                     triggerAction: 'all',
40878                     emptyText:'Select',
40879                     selectOnFocus:true,
40880                     width: item.width ? item.width  : 130,
40881                     listeners : {
40882                         'select': function(c, r, i) {
40883                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40884                         }
40885                     }
40886
40887                 }));
40888                 continue;
40889                     
40890                  
40891                 
40892                 tb.addField( new Roo.form.TextField({
40893                     name: i,
40894                     width: 100,
40895                     //allowBlank:false,
40896                     value: ''
40897                 }));
40898                 continue;
40899             }
40900             tb.addField( new Roo.form.TextField({
40901                 name: i,
40902                 width: item.width,
40903                 //allowBlank:true,
40904                 value: '',
40905                 listeners: {
40906                     'change' : function(f, nv, ov) {
40907                         tb.selectedNode.setAttribute(f.name, nv);
40908                     }
40909                 }
40910             }));
40911              
40912         }
40913         tb.el.on('click', function(e){
40914             e.preventDefault(); // what does this do?
40915         });
40916         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40917         tb.el.hide();
40918         tb.name = nm;
40919         // dont need to disable them... as they will get hidden
40920         return tb;
40921          
40922         
40923     },
40924     buildFooter : function()
40925     {
40926         
40927         var fel = this.editor.wrap.createChild();
40928         this.footer = new Roo.Toolbar(fel);
40929         // toolbar has scrolly on left / right?
40930         var footDisp= new Roo.Toolbar.Fill();
40931         var _t = this;
40932         this.footer.add(
40933             {
40934                 text : '&lt;',
40935                 xtype: 'Button',
40936                 handler : function() {
40937                     _t.footDisp.scrollTo('left',0,true)
40938                 }
40939             }
40940         );
40941         this.footer.add( footDisp );
40942         this.footer.add( 
40943             {
40944                 text : '&gt;',
40945                 xtype: 'Button',
40946                 handler : function() {
40947                     // no animation..
40948                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40949                 }
40950             }
40951         );
40952         var fel = Roo.get(footDisp.el);
40953         fel.addClass('x-editor-context');
40954         this.footDispWrap = fel; 
40955         this.footDispWrap.overflow  = 'hidden';
40956         
40957         this.footDisp = fel.createChild();
40958         this.footDispWrap.on('click', this.onContextClick, this)
40959         
40960         
40961     },
40962     onContextClick : function (ev,dom)
40963     {
40964         ev.preventDefault();
40965         var  cn = dom.className;
40966         Roo.log(cn);
40967         if (!cn.match(/x-ed-loc-/)) {
40968             return;
40969         }
40970         var n = cn.split('-').pop();
40971         var ans = this.footerEls;
40972         var sel = ans[n];
40973         
40974          // pick
40975         var range = this.editor.createRange();
40976         
40977         range.selectNodeContents(sel);
40978         //range.selectNode(sel);
40979         
40980         
40981         var selection = this.editor.getSelection();
40982         selection.removeAllRanges();
40983         selection.addRange(range);
40984         
40985         
40986         
40987         this.updateToolbar(null, null, sel);
40988         
40989         
40990     }
40991     
40992     
40993     
40994     
40995     
40996 });
40997
40998
40999
41000
41001
41002 /*
41003  * Based on:
41004  * Ext JS Library 1.1.1
41005  * Copyright(c) 2006-2007, Ext JS, LLC.
41006  *
41007  * Originally Released Under LGPL - original licence link has changed is not relivant.
41008  *
41009  * Fork - LGPL
41010  * <script type="text/javascript">
41011  */
41012  
41013 /**
41014  * @class Roo.form.BasicForm
41015  * @extends Roo.util.Observable
41016  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41017  * @constructor
41018  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41019  * @param {Object} config Configuration options
41020  */
41021 Roo.form.BasicForm = function(el, config){
41022     this.allItems = [];
41023     this.childForms = [];
41024     Roo.apply(this, config);
41025     /*
41026      * The Roo.form.Field items in this form.
41027      * @type MixedCollection
41028      */
41029      
41030      
41031     this.items = new Roo.util.MixedCollection(false, function(o){
41032         return o.id || (o.id = Roo.id());
41033     });
41034     this.addEvents({
41035         /**
41036          * @event beforeaction
41037          * Fires before any action is performed. Return false to cancel the action.
41038          * @param {Form} this
41039          * @param {Action} action The action to be performed
41040          */
41041         beforeaction: true,
41042         /**
41043          * @event actionfailed
41044          * Fires when an action fails.
41045          * @param {Form} this
41046          * @param {Action} action The action that failed
41047          */
41048         actionfailed : true,
41049         /**
41050          * @event actioncomplete
41051          * Fires when an action is completed.
41052          * @param {Form} this
41053          * @param {Action} action The action that completed
41054          */
41055         actioncomplete : true
41056     });
41057     if(el){
41058         this.initEl(el);
41059     }
41060     Roo.form.BasicForm.superclass.constructor.call(this);
41061 };
41062
41063 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41064     /**
41065      * @cfg {String} method
41066      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41067      */
41068     /**
41069      * @cfg {DataReader} reader
41070      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41071      * This is optional as there is built-in support for processing JSON.
41072      */
41073     /**
41074      * @cfg {DataReader} errorReader
41075      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41076      * This is completely optional as there is built-in support for processing JSON.
41077      */
41078     /**
41079      * @cfg {String} url
41080      * The URL to use for form actions if one isn't supplied in the action options.
41081      */
41082     /**
41083      * @cfg {Boolean} fileUpload
41084      * Set to true if this form is a file upload.
41085      */
41086      
41087     /**
41088      * @cfg {Object} baseParams
41089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41090      */
41091      /**
41092      
41093     /**
41094      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41095      */
41096     timeout: 30,
41097
41098     // private
41099     activeAction : null,
41100
41101     /**
41102      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41103      * or setValues() data instead of when the form was first created.
41104      */
41105     trackResetOnLoad : false,
41106     
41107     
41108     /**
41109      * childForms - used for multi-tab forms
41110      * @type {Array}
41111      */
41112     childForms : false,
41113     
41114     /**
41115      * allItems - full list of fields.
41116      * @type {Array}
41117      */
41118     allItems : false,
41119     
41120     /**
41121      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41122      * element by passing it or its id or mask the form itself by passing in true.
41123      * @type Mixed
41124      */
41125     waitMsgTarget : false,
41126
41127     // private
41128     initEl : function(el){
41129         this.el = Roo.get(el);
41130         this.id = this.el.id || Roo.id();
41131         this.el.on('submit', this.onSubmit, this);
41132         this.el.addClass('x-form');
41133     },
41134
41135     // private
41136     onSubmit : function(e){
41137         e.stopEvent();
41138     },
41139
41140     /**
41141      * Returns true if client-side validation on the form is successful.
41142      * @return Boolean
41143      */
41144     isValid : function(){
41145         var valid = true;
41146         this.items.each(function(f){
41147            if(!f.validate()){
41148                valid = false;
41149            }
41150         });
41151         return valid;
41152     },
41153
41154     /**
41155      * Returns true if any fields in this form have changed since their original load.
41156      * @return Boolean
41157      */
41158     isDirty : function(){
41159         var dirty = false;
41160         this.items.each(function(f){
41161            if(f.isDirty()){
41162                dirty = true;
41163                return false;
41164            }
41165         });
41166         return dirty;
41167     },
41168
41169     /**
41170      * Performs a predefined action (submit or load) or custom actions you define on this form.
41171      * @param {String} actionName The name of the action type
41172      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41173      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41174      * accept other config options):
41175      * <pre>
41176 Property          Type             Description
41177 ----------------  ---------------  ----------------------------------------------------------------------------------
41178 url               String           The url for the action (defaults to the form's url)
41179 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41180 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41181 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41182                                    validate the form on the client (defaults to false)
41183      * </pre>
41184      * @return {BasicForm} this
41185      */
41186     doAction : function(action, options){
41187         if(typeof action == 'string'){
41188             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41189         }
41190         if(this.fireEvent('beforeaction', this, action) !== false){
41191             this.beforeAction(action);
41192             action.run.defer(100, action);
41193         }
41194         return this;
41195     },
41196
41197     /**
41198      * Shortcut to do a submit action.
41199      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41200      * @return {BasicForm} this
41201      */
41202     submit : function(options){
41203         this.doAction('submit', options);
41204         return this;
41205     },
41206
41207     /**
41208      * Shortcut to do a load action.
41209      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41210      * @return {BasicForm} this
41211      */
41212     load : function(options){
41213         this.doAction('load', options);
41214         return this;
41215     },
41216
41217     /**
41218      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41219      * @param {Record} record The record to edit
41220      * @return {BasicForm} this
41221      */
41222     updateRecord : function(record){
41223         record.beginEdit();
41224         var fs = record.fields;
41225         fs.each(function(f){
41226             var field = this.findField(f.name);
41227             if(field){
41228                 record.set(f.name, field.getValue());
41229             }
41230         }, this);
41231         record.endEdit();
41232         return this;
41233     },
41234
41235     /**
41236      * Loads an Roo.data.Record into this form.
41237      * @param {Record} record The record to load
41238      * @return {BasicForm} this
41239      */
41240     loadRecord : function(record){
41241         this.setValues(record.data);
41242         return this;
41243     },
41244
41245     // private
41246     beforeAction : function(action){
41247         var o = action.options;
41248         
41249        
41250         if(this.waitMsgTarget === true){
41251             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41252         }else if(this.waitMsgTarget){
41253             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41254             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41255         }else {
41256             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41257         }
41258          
41259     },
41260
41261     // private
41262     afterAction : function(action, success){
41263         this.activeAction = null;
41264         var o = action.options;
41265         
41266         if(this.waitMsgTarget === true){
41267             this.el.unmask();
41268         }else if(this.waitMsgTarget){
41269             this.waitMsgTarget.unmask();
41270         }else{
41271             Roo.MessageBox.updateProgress(1);
41272             Roo.MessageBox.hide();
41273         }
41274          
41275         if(success){
41276             if(o.reset){
41277                 this.reset();
41278             }
41279             Roo.callback(o.success, o.scope, [this, action]);
41280             this.fireEvent('actioncomplete', this, action);
41281             
41282         }else{
41283             Roo.callback(o.failure, o.scope, [this, action]);
41284             // show an error message if no failed handler is set..
41285             if (!this.hasListener('actionfailed')) {
41286                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41287             }
41288             
41289             this.fireEvent('actionfailed', this, action);
41290         }
41291         
41292     },
41293
41294     /**
41295      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41296      * @param {String} id The value to search for
41297      * @return Field
41298      */
41299     findField : function(id){
41300         var field = this.items.get(id);
41301         if(!field){
41302             this.items.each(function(f){
41303                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41304                     field = f;
41305                     return false;
41306                 }
41307             });
41308         }
41309         return field || null;
41310     },
41311
41312     /**
41313      * Add a secondary form to this one, 
41314      * Used to provide tabbed forms. One form is primary, with hidden values 
41315      * which mirror the elements from the other forms.
41316      * 
41317      * @param {Roo.form.Form} form to add.
41318      * 
41319      */
41320     addForm : function(form)
41321     {
41322        
41323         if (this.childForms.indexOf(form) > -1) {
41324             // already added..
41325             return;
41326         }
41327         this.childForms.push(form);
41328         var n = '';
41329         Roo.each(form.allItems, function (fe) {
41330             
41331             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41332             if (this.findField(n)) { // already added..
41333                 return;
41334             }
41335             var add = new Roo.form.Hidden({
41336                 name : n
41337             });
41338             add.render(this.el);
41339             
41340             this.add( add );
41341         }, this);
41342         
41343     },
41344     /**
41345      * Mark fields in this form invalid in bulk.
41346      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41347      * @return {BasicForm} this
41348      */
41349     markInvalid : function(errors){
41350         if(errors instanceof Array){
41351             for(var i = 0, len = errors.length; i < len; i++){
41352                 var fieldError = errors[i];
41353                 var f = this.findField(fieldError.id);
41354                 if(f){
41355                     f.markInvalid(fieldError.msg);
41356                 }
41357             }
41358         }else{
41359             var field, id;
41360             for(id in errors){
41361                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41362                     field.markInvalid(errors[id]);
41363                 }
41364             }
41365         }
41366         Roo.each(this.childForms || [], function (f) {
41367             f.markInvalid(errors);
41368         });
41369         
41370         return this;
41371     },
41372
41373     /**
41374      * Set values for fields in this form in bulk.
41375      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41376      * @return {BasicForm} this
41377      */
41378     setValues : function(values){
41379         if(values instanceof Array){ // array of objects
41380             for(var i = 0, len = values.length; i < len; i++){
41381                 var v = values[i];
41382                 var f = this.findField(v.id);
41383                 if(f){
41384                     f.setValue(v.value);
41385                     if(this.trackResetOnLoad){
41386                         f.originalValue = f.getValue();
41387                     }
41388                 }
41389             }
41390         }else{ // object hash
41391             var field, id;
41392             for(id in values){
41393                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41394                     
41395                     if (field.setFromData && 
41396                         field.valueField && 
41397                         field.displayField &&
41398                         // combos' with local stores can 
41399                         // be queried via setValue()
41400                         // to set their value..
41401                         (field.store && !field.store.isLocal)
41402                         ) {
41403                         // it's a combo
41404                         var sd = { };
41405                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41406                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41407                         field.setFromData(sd);
41408                         
41409                     } else {
41410                         field.setValue(values[id]);
41411                     }
41412                     
41413                     
41414                     if(this.trackResetOnLoad){
41415                         field.originalValue = field.getValue();
41416                     }
41417                 }
41418             }
41419         }
41420          
41421         Roo.each(this.childForms || [], function (f) {
41422             f.setValues(values);
41423         });
41424                 
41425         return this;
41426     },
41427
41428     /**
41429      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41430      * they are returned as an array.
41431      * @param {Boolean} asString
41432      * @return {Object}
41433      */
41434     getValues : function(asString){
41435         if (this.childForms) {
41436             // copy values from the child forms
41437             Roo.each(this.childForms, function (f) {
41438                 this.setValues(f.getValues());
41439             }, this);
41440         }
41441         
41442         
41443         
41444         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41445         if(asString === true){
41446             return fs;
41447         }
41448         return Roo.urlDecode(fs);
41449     },
41450     
41451     /**
41452      * Returns the fields in this form as an object with key/value pairs. 
41453      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41454      * @return {Object}
41455      */
41456     getFieldValues : function()
41457     {
41458         if (this.childForms) {
41459             // copy values from the child forms
41460             Roo.each(this.childForms, function (f) {
41461                 this.setValues(f.getValues());
41462             }, this);
41463         }
41464         
41465         var ret = {};
41466         this.items.each(function(f){
41467             if (!f.getName()) {
41468                 return;
41469             }
41470             var v = f.getValue();
41471             if ((typeof(v) == 'object') && f.getRawValue) {
41472                 v = f.getRawValue() ; // dates..
41473             }
41474             ret[f.getName()] = v;
41475         });
41476         
41477         return ret;
41478     },
41479
41480     /**
41481      * Clears all invalid messages in this form.
41482      * @return {BasicForm} this
41483      */
41484     clearInvalid : function(){
41485         this.items.each(function(f){
41486            f.clearInvalid();
41487         });
41488         
41489         Roo.each(this.childForms || [], function (f) {
41490             f.clearInvalid();
41491         });
41492         
41493         
41494         return this;
41495     },
41496
41497     /**
41498      * Resets this form.
41499      * @return {BasicForm} this
41500      */
41501     reset : function(){
41502         this.items.each(function(f){
41503             f.reset();
41504         });
41505         
41506         Roo.each(this.childForms || [], function (f) {
41507             f.reset();
41508         });
41509        
41510         
41511         return this;
41512     },
41513
41514     /**
41515      * Add Roo.form components to this form.
41516      * @param {Field} field1
41517      * @param {Field} field2 (optional)
41518      * @param {Field} etc (optional)
41519      * @return {BasicForm} this
41520      */
41521     add : function(){
41522         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41523         return this;
41524     },
41525
41526
41527     /**
41528      * Removes a field from the items collection (does NOT remove its markup).
41529      * @param {Field} field
41530      * @return {BasicForm} this
41531      */
41532     remove : function(field){
41533         this.items.remove(field);
41534         return this;
41535     },
41536
41537     /**
41538      * Looks at the fields in this form, checks them for an id attribute,
41539      * and calls applyTo on the existing dom element with that id.
41540      * @return {BasicForm} this
41541      */
41542     render : function(){
41543         this.items.each(function(f){
41544             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41545                 f.applyTo(f.id);
41546             }
41547         });
41548         return this;
41549     },
41550
41551     /**
41552      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41553      * @param {Object} values
41554      * @return {BasicForm} this
41555      */
41556     applyToFields : function(o){
41557         this.items.each(function(f){
41558            Roo.apply(f, o);
41559         });
41560         return this;
41561     },
41562
41563     /**
41564      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41565      * @param {Object} values
41566      * @return {BasicForm} this
41567      */
41568     applyIfToFields : function(o){
41569         this.items.each(function(f){
41570            Roo.applyIf(f, o);
41571         });
41572         return this;
41573     }
41574 });
41575
41576 // back compat
41577 Roo.BasicForm = Roo.form.BasicForm;/*
41578  * Based on:
41579  * Ext JS Library 1.1.1
41580  * Copyright(c) 2006-2007, Ext JS, LLC.
41581  *
41582  * Originally Released Under LGPL - original licence link has changed is not relivant.
41583  *
41584  * Fork - LGPL
41585  * <script type="text/javascript">
41586  */
41587
41588 /**
41589  * @class Roo.form.Form
41590  * @extends Roo.form.BasicForm
41591  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41592  * @constructor
41593  * @param {Object} config Configuration options
41594  */
41595 Roo.form.Form = function(config){
41596     var xitems =  [];
41597     if (config.items) {
41598         xitems = config.items;
41599         delete config.items;
41600     }
41601    
41602     
41603     Roo.form.Form.superclass.constructor.call(this, null, config);
41604     this.url = this.url || this.action;
41605     if(!this.root){
41606         this.root = new Roo.form.Layout(Roo.applyIf({
41607             id: Roo.id()
41608         }, config));
41609     }
41610     this.active = this.root;
41611     /**
41612      * Array of all the buttons that have been added to this form via {@link addButton}
41613      * @type Array
41614      */
41615     this.buttons = [];
41616     this.allItems = [];
41617     this.addEvents({
41618         /**
41619          * @event clientvalidation
41620          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41621          * @param {Form} this
41622          * @param {Boolean} valid true if the form has passed client-side validation
41623          */
41624         clientvalidation: true,
41625         /**
41626          * @event rendered
41627          * Fires when the form is rendered
41628          * @param {Roo.form.Form} form
41629          */
41630         rendered : true
41631     });
41632     
41633     if (this.progressUrl) {
41634             // push a hidden field onto the list of fields..
41635             this.addxtype( {
41636                     xns: Roo.form, 
41637                     xtype : 'Hidden', 
41638                     name : 'UPLOAD_IDENTIFIER' 
41639             });
41640         }
41641         
41642     
41643     Roo.each(xitems, this.addxtype, this);
41644     
41645     
41646     
41647 };
41648
41649 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41650     /**
41651      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41652      */
41653     /**
41654      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41655      */
41656     /**
41657      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41658      */
41659     buttonAlign:'center',
41660
41661     /**
41662      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41663      */
41664     minButtonWidth:75,
41665
41666     /**
41667      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41668      * This property cascades to child containers if not set.
41669      */
41670     labelAlign:'left',
41671
41672     /**
41673      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41674      * fires a looping event with that state. This is required to bind buttons to the valid
41675      * state using the config value formBind:true on the button.
41676      */
41677     monitorValid : false,
41678
41679     /**
41680      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41681      */
41682     monitorPoll : 200,
41683     
41684     /**
41685      * @cfg {String} progressUrl - Url to return progress data 
41686      */
41687     
41688     progressUrl : false,
41689   
41690     /**
41691      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41692      * fields are added and the column is closed. If no fields are passed the column remains open
41693      * until end() is called.
41694      * @param {Object} config The config to pass to the column
41695      * @param {Field} field1 (optional)
41696      * @param {Field} field2 (optional)
41697      * @param {Field} etc (optional)
41698      * @return Column The column container object
41699      */
41700     column : function(c){
41701         var col = new Roo.form.Column(c);
41702         this.start(col);
41703         if(arguments.length > 1){ // duplicate code required because of Opera
41704             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41705             this.end();
41706         }
41707         return col;
41708     },
41709
41710     /**
41711      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41712      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41713      * until end() is called.
41714      * @param {Object} config The config to pass to the fieldset
41715      * @param {Field} field1 (optional)
41716      * @param {Field} field2 (optional)
41717      * @param {Field} etc (optional)
41718      * @return FieldSet The fieldset container object
41719      */
41720     fieldset : function(c){
41721         var fs = new Roo.form.FieldSet(c);
41722         this.start(fs);
41723         if(arguments.length > 1){ // duplicate code required because of Opera
41724             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41725             this.end();
41726         }
41727         return fs;
41728     },
41729
41730     /**
41731      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41732      * fields are added and the container is closed. If no fields are passed the container remains open
41733      * until end() is called.
41734      * @param {Object} config The config to pass to the Layout
41735      * @param {Field} field1 (optional)
41736      * @param {Field} field2 (optional)
41737      * @param {Field} etc (optional)
41738      * @return Layout The container object
41739      */
41740     container : function(c){
41741         var l = new Roo.form.Layout(c);
41742         this.start(l);
41743         if(arguments.length > 1){ // duplicate code required because of Opera
41744             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41745             this.end();
41746         }
41747         return l;
41748     },
41749
41750     /**
41751      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41752      * @param {Object} container A Roo.form.Layout or subclass of Layout
41753      * @return {Form} this
41754      */
41755     start : function(c){
41756         // cascade label info
41757         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41758         this.active.stack.push(c);
41759         c.ownerCt = this.active;
41760         this.active = c;
41761         return this;
41762     },
41763
41764     /**
41765      * Closes the current open container
41766      * @return {Form} this
41767      */
41768     end : function(){
41769         if(this.active == this.root){
41770             return this;
41771         }
41772         this.active = this.active.ownerCt;
41773         return this;
41774     },
41775
41776     /**
41777      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41778      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41779      * as the label of the field.
41780      * @param {Field} field1
41781      * @param {Field} field2 (optional)
41782      * @param {Field} etc. (optional)
41783      * @return {Form} this
41784      */
41785     add : function(){
41786         this.active.stack.push.apply(this.active.stack, arguments);
41787         this.allItems.push.apply(this.allItems,arguments);
41788         var r = [];
41789         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41790             if(a[i].isFormField){
41791                 r.push(a[i]);
41792             }
41793         }
41794         if(r.length > 0){
41795             Roo.form.Form.superclass.add.apply(this, r);
41796         }
41797         return this;
41798     },
41799     
41800
41801     
41802     
41803     
41804      /**
41805      * Find any element that has been added to a form, using it's ID or name
41806      * This can include framesets, columns etc. along with regular fields..
41807      * @param {String} id - id or name to find.
41808      
41809      * @return {Element} e - or false if nothing found.
41810      */
41811     findbyId : function(id)
41812     {
41813         var ret = false;
41814         if (!id) {
41815             return ret;
41816         }
41817         Roo.each(this.allItems, function(f){
41818             if (f.id == id || f.name == id ){
41819                 ret = f;
41820                 return false;
41821             }
41822         });
41823         return ret;
41824     },
41825
41826     
41827     
41828     /**
41829      * Render this form into the passed container. This should only be called once!
41830      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41831      * @return {Form} this
41832      */
41833     render : function(ct)
41834     {
41835         
41836         
41837         
41838         ct = Roo.get(ct);
41839         var o = this.autoCreate || {
41840             tag: 'form',
41841             method : this.method || 'POST',
41842             id : this.id || Roo.id()
41843         };
41844         this.initEl(ct.createChild(o));
41845
41846         this.root.render(this.el);
41847         
41848        
41849              
41850         this.items.each(function(f){
41851             f.render('x-form-el-'+f.id);
41852         });
41853
41854         if(this.buttons.length > 0){
41855             // tables are required to maintain order and for correct IE layout
41856             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41857                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41858                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41859             }}, null, true);
41860             var tr = tb.getElementsByTagName('tr')[0];
41861             for(var i = 0, len = this.buttons.length; i < len; i++) {
41862                 var b = this.buttons[i];
41863                 var td = document.createElement('td');
41864                 td.className = 'x-form-btn-td';
41865                 b.render(tr.appendChild(td));
41866             }
41867         }
41868         if(this.monitorValid){ // initialize after render
41869             this.startMonitoring();
41870         }
41871         this.fireEvent('rendered', this);
41872         return this;
41873     },
41874
41875     /**
41876      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41877      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41878      * object or a valid Roo.DomHelper element config
41879      * @param {Function} handler The function called when the button is clicked
41880      * @param {Object} scope (optional) The scope of the handler function
41881      * @return {Roo.Button}
41882      */
41883     addButton : function(config, handler, scope){
41884         var bc = {
41885             handler: handler,
41886             scope: scope,
41887             minWidth: this.minButtonWidth,
41888             hideParent:true
41889         };
41890         if(typeof config == "string"){
41891             bc.text = config;
41892         }else{
41893             Roo.apply(bc, config);
41894         }
41895         var btn = new Roo.Button(null, bc);
41896         this.buttons.push(btn);
41897         return btn;
41898     },
41899
41900      /**
41901      * Adds a series of form elements (using the xtype property as the factory method.
41902      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41903      * @param {Object} config 
41904      */
41905     
41906     addxtype : function()
41907     {
41908         var ar = Array.prototype.slice.call(arguments, 0);
41909         var ret = false;
41910         for(var i = 0; i < ar.length; i++) {
41911             if (!ar[i]) {
41912                 continue; // skip -- if this happends something invalid got sent, we 
41913                 // should ignore it, as basically that interface element will not show up
41914                 // and that should be pretty obvious!!
41915             }
41916             
41917             if (Roo.form[ar[i].xtype]) {
41918                 ar[i].form = this;
41919                 var fe = Roo.factory(ar[i], Roo.form);
41920                 if (!ret) {
41921                     ret = fe;
41922                 }
41923                 fe.form = this;
41924                 if (fe.store) {
41925                     fe.store.form = this;
41926                 }
41927                 if (fe.isLayout) {  
41928                          
41929                     this.start(fe);
41930                     this.allItems.push(fe);
41931                     if (fe.items && fe.addxtype) {
41932                         fe.addxtype.apply(fe, fe.items);
41933                         delete fe.items;
41934                     }
41935                      this.end();
41936                     continue;
41937                 }
41938                 
41939                 
41940                  
41941                 this.add(fe);
41942               //  console.log('adding ' + ar[i].xtype);
41943             }
41944             if (ar[i].xtype == 'Button') {  
41945                 //console.log('adding button');
41946                 //console.log(ar[i]);
41947                 this.addButton(ar[i]);
41948                 this.allItems.push(fe);
41949                 continue;
41950             }
41951             
41952             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41953                 alert('end is not supported on xtype any more, use items');
41954             //    this.end();
41955             //    //console.log('adding end');
41956             }
41957             
41958         }
41959         return ret;
41960     },
41961     
41962     /**
41963      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41964      * option "monitorValid"
41965      */
41966     startMonitoring : function(){
41967         if(!this.bound){
41968             this.bound = true;
41969             Roo.TaskMgr.start({
41970                 run : this.bindHandler,
41971                 interval : this.monitorPoll || 200,
41972                 scope: this
41973             });
41974         }
41975     },
41976
41977     /**
41978      * Stops monitoring of the valid state of this form
41979      */
41980     stopMonitoring : function(){
41981         this.bound = false;
41982     },
41983
41984     // private
41985     bindHandler : function(){
41986         if(!this.bound){
41987             return false; // stops binding
41988         }
41989         var valid = true;
41990         this.items.each(function(f){
41991             if(!f.isValid(true)){
41992                 valid = false;
41993                 return false;
41994             }
41995         });
41996         for(var i = 0, len = this.buttons.length; i < len; i++){
41997             var btn = this.buttons[i];
41998             if(btn.formBind === true && btn.disabled === valid){
41999                 btn.setDisabled(!valid);
42000             }
42001         }
42002         this.fireEvent('clientvalidation', this, valid);
42003     }
42004     
42005     
42006     
42007     
42008     
42009     
42010     
42011     
42012 });
42013
42014
42015 // back compat
42016 Roo.Form = Roo.form.Form;
42017 /*
42018  * Based on:
42019  * Ext JS Library 1.1.1
42020  * Copyright(c) 2006-2007, Ext JS, LLC.
42021  *
42022  * Originally Released Under LGPL - original licence link has changed is not relivant.
42023  *
42024  * Fork - LGPL
42025  * <script type="text/javascript">
42026  */
42027  
42028  /**
42029  * @class Roo.form.Action
42030  * Internal Class used to handle form actions
42031  * @constructor
42032  * @param {Roo.form.BasicForm} el The form element or its id
42033  * @param {Object} config Configuration options
42034  */
42035  
42036  
42037 // define the action interface
42038 Roo.form.Action = function(form, options){
42039     this.form = form;
42040     this.options = options || {};
42041 };
42042 /**
42043  * Client Validation Failed
42044  * @const 
42045  */
42046 Roo.form.Action.CLIENT_INVALID = 'client';
42047 /**
42048  * Server Validation Failed
42049  * @const 
42050  */
42051  Roo.form.Action.SERVER_INVALID = 'server';
42052  /**
42053  * Connect to Server Failed
42054  * @const 
42055  */
42056 Roo.form.Action.CONNECT_FAILURE = 'connect';
42057 /**
42058  * Reading Data from Server Failed
42059  * @const 
42060  */
42061 Roo.form.Action.LOAD_FAILURE = 'load';
42062
42063 Roo.form.Action.prototype = {
42064     type : 'default',
42065     failureType : undefined,
42066     response : undefined,
42067     result : undefined,
42068
42069     // interface method
42070     run : function(options){
42071
42072     },
42073
42074     // interface method
42075     success : function(response){
42076
42077     },
42078
42079     // interface method
42080     handleResponse : function(response){
42081
42082     },
42083
42084     // default connection failure
42085     failure : function(response){
42086         
42087         this.response = response;
42088         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42089         this.form.afterAction(this, false);
42090     },
42091
42092     processResponse : function(response){
42093         this.response = response;
42094         if(!response.responseText){
42095             return true;
42096         }
42097         this.result = this.handleResponse(response);
42098         return this.result;
42099     },
42100
42101     // utility functions used internally
42102     getUrl : function(appendParams){
42103         var url = this.options.url || this.form.url || this.form.el.dom.action;
42104         if(appendParams){
42105             var p = this.getParams();
42106             if(p){
42107                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42108             }
42109         }
42110         return url;
42111     },
42112
42113     getMethod : function(){
42114         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42115     },
42116
42117     getParams : function(){
42118         var bp = this.form.baseParams;
42119         var p = this.options.params;
42120         if(p){
42121             if(typeof p == "object"){
42122                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42123             }else if(typeof p == 'string' && bp){
42124                 p += '&' + Roo.urlEncode(bp);
42125             }
42126         }else if(bp){
42127             p = Roo.urlEncode(bp);
42128         }
42129         return p;
42130     },
42131
42132     createCallback : function(){
42133         return {
42134             success: this.success,
42135             failure: this.failure,
42136             scope: this,
42137             timeout: (this.form.timeout*1000),
42138             upload: this.form.fileUpload ? this.success : undefined
42139         };
42140     }
42141 };
42142
42143 Roo.form.Action.Submit = function(form, options){
42144     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42145 };
42146
42147 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42148     type : 'submit',
42149
42150     haveProgress : false,
42151     uploadComplete : false,
42152     
42153     // uploadProgress indicator.
42154     uploadProgress : function()
42155     {
42156         if (!this.form.progressUrl) {
42157             return;
42158         }
42159         
42160         if (!this.haveProgress) {
42161             Roo.MessageBox.progress("Uploading", "Uploading");
42162         }
42163         if (this.uploadComplete) {
42164            Roo.MessageBox.hide();
42165            return;
42166         }
42167         
42168         this.haveProgress = true;
42169    
42170         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42171         
42172         var c = new Roo.data.Connection();
42173         c.request({
42174             url : this.form.progressUrl,
42175             params: {
42176                 id : uid
42177             },
42178             method: 'GET',
42179             success : function(req){
42180                //console.log(data);
42181                 var rdata = false;
42182                 var edata;
42183                 try  {
42184                    rdata = Roo.decode(req.responseText)
42185                 } catch (e) {
42186                     Roo.log("Invalid data from server..");
42187                     Roo.log(edata);
42188                     return;
42189                 }
42190                 if (!rdata || !rdata.success) {
42191                     Roo.log(rdata);
42192                     return;
42193                 }
42194                 var data = rdata.data;
42195                 
42196                 if (this.uploadComplete) {
42197                    Roo.MessageBox.hide();
42198                    return;
42199                 }
42200                    
42201                 if (data){
42202                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42203                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42204                     );
42205                 }
42206                 this.uploadProgress.defer(2000,this);
42207             },
42208        
42209             failure: function(data) {
42210                 Roo.log('progress url failed ');
42211                 Roo.log(data);
42212             },
42213             scope : this
42214         });
42215            
42216     },
42217     
42218     
42219     run : function()
42220     {
42221         // run get Values on the form, so it syncs any secondary forms.
42222         this.form.getValues();
42223         
42224         var o = this.options;
42225         var method = this.getMethod();
42226         var isPost = method == 'POST';
42227         if(o.clientValidation === false || this.form.isValid()){
42228             
42229             if (this.form.progressUrl) {
42230                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42231                     (new Date() * 1) + '' + Math.random());
42232                     
42233             } 
42234             
42235             
42236             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42237                 form:this.form.el.dom,
42238                 url:this.getUrl(!isPost),
42239                 method: method,
42240                 params:isPost ? this.getParams() : null,
42241                 isUpload: this.form.fileUpload
42242             }));
42243             
42244             this.uploadProgress();
42245
42246         }else if (o.clientValidation !== false){ // client validation failed
42247             this.failureType = Roo.form.Action.CLIENT_INVALID;
42248             this.form.afterAction(this, false);
42249         }
42250     },
42251
42252     success : function(response)
42253     {
42254         this.uploadComplete= true;
42255         if (this.haveProgress) {
42256             Roo.MessageBox.hide();
42257         }
42258         
42259         
42260         var result = this.processResponse(response);
42261         if(result === true || result.success){
42262             this.form.afterAction(this, true);
42263             return;
42264         }
42265         if(result.errors){
42266             this.form.markInvalid(result.errors);
42267             this.failureType = Roo.form.Action.SERVER_INVALID;
42268         }
42269         this.form.afterAction(this, false);
42270     },
42271     failure : function(response)
42272     {
42273         this.uploadComplete= true;
42274         if (this.haveProgress) {
42275             Roo.MessageBox.hide();
42276         }
42277         
42278         
42279         this.response = response;
42280         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42281         this.form.afterAction(this, false);
42282     },
42283     
42284     handleResponse : function(response){
42285         if(this.form.errorReader){
42286             var rs = this.form.errorReader.read(response);
42287             var errors = [];
42288             if(rs.records){
42289                 for(var i = 0, len = rs.records.length; i < len; i++) {
42290                     var r = rs.records[i];
42291                     errors[i] = r.data;
42292                 }
42293             }
42294             if(errors.length < 1){
42295                 errors = null;
42296             }
42297             return {
42298                 success : rs.success,
42299                 errors : errors
42300             };
42301         }
42302         var ret = false;
42303         try {
42304             ret = Roo.decode(response.responseText);
42305         } catch (e) {
42306             ret = {
42307                 success: false,
42308                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42309                 errors : []
42310             };
42311         }
42312         return ret;
42313         
42314     }
42315 });
42316
42317
42318 Roo.form.Action.Load = function(form, options){
42319     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42320     this.reader = this.form.reader;
42321 };
42322
42323 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42324     type : 'load',
42325
42326     run : function(){
42327         
42328         Roo.Ajax.request(Roo.apply(
42329                 this.createCallback(), {
42330                     method:this.getMethod(),
42331                     url:this.getUrl(false),
42332                     params:this.getParams()
42333         }));
42334     },
42335
42336     success : function(response){
42337         
42338         var result = this.processResponse(response);
42339         if(result === true || !result.success || !result.data){
42340             this.failureType = Roo.form.Action.LOAD_FAILURE;
42341             this.form.afterAction(this, false);
42342             return;
42343         }
42344         this.form.clearInvalid();
42345         this.form.setValues(result.data);
42346         this.form.afterAction(this, true);
42347     },
42348
42349     handleResponse : function(response){
42350         if(this.form.reader){
42351             var rs = this.form.reader.read(response);
42352             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42353             return {
42354                 success : rs.success,
42355                 data : data
42356             };
42357         }
42358         return Roo.decode(response.responseText);
42359     }
42360 });
42361
42362 Roo.form.Action.ACTION_TYPES = {
42363     'load' : Roo.form.Action.Load,
42364     'submit' : Roo.form.Action.Submit
42365 };/*
42366  * Based on:
42367  * Ext JS Library 1.1.1
42368  * Copyright(c) 2006-2007, Ext JS, LLC.
42369  *
42370  * Originally Released Under LGPL - original licence link has changed is not relivant.
42371  *
42372  * Fork - LGPL
42373  * <script type="text/javascript">
42374  */
42375  
42376 /**
42377  * @class Roo.form.Layout
42378  * @extends Roo.Component
42379  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42380  * @constructor
42381  * @param {Object} config Configuration options
42382  */
42383 Roo.form.Layout = function(config){
42384     var xitems = [];
42385     if (config.items) {
42386         xitems = config.items;
42387         delete config.items;
42388     }
42389     Roo.form.Layout.superclass.constructor.call(this, config);
42390     this.stack = [];
42391     Roo.each(xitems, this.addxtype, this);
42392      
42393 };
42394
42395 Roo.extend(Roo.form.Layout, Roo.Component, {
42396     /**
42397      * @cfg {String/Object} autoCreate
42398      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42399      */
42400     /**
42401      * @cfg {String/Object/Function} style
42402      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42403      * a function which returns such a specification.
42404      */
42405     /**
42406      * @cfg {String} labelAlign
42407      * Valid values are "left," "top" and "right" (defaults to "left")
42408      */
42409     /**
42410      * @cfg {Number} labelWidth
42411      * Fixed width in pixels of all field labels (defaults to undefined)
42412      */
42413     /**
42414      * @cfg {Boolean} clear
42415      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42416      */
42417     clear : true,
42418     /**
42419      * @cfg {String} labelSeparator
42420      * The separator to use after field labels (defaults to ':')
42421      */
42422     labelSeparator : ':',
42423     /**
42424      * @cfg {Boolean} hideLabels
42425      * True to suppress the display of field labels in this layout (defaults to false)
42426      */
42427     hideLabels : false,
42428
42429     // private
42430     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42431     
42432     isLayout : true,
42433     
42434     // private
42435     onRender : function(ct, position){
42436         if(this.el){ // from markup
42437             this.el = Roo.get(this.el);
42438         }else {  // generate
42439             var cfg = this.getAutoCreate();
42440             this.el = ct.createChild(cfg, position);
42441         }
42442         if(this.style){
42443             this.el.applyStyles(this.style);
42444         }
42445         if(this.labelAlign){
42446             this.el.addClass('x-form-label-'+this.labelAlign);
42447         }
42448         if(this.hideLabels){
42449             this.labelStyle = "display:none";
42450             this.elementStyle = "padding-left:0;";
42451         }else{
42452             if(typeof this.labelWidth == 'number'){
42453                 this.labelStyle = "width:"+this.labelWidth+"px;";
42454                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42455             }
42456             if(this.labelAlign == 'top'){
42457                 this.labelStyle = "width:auto;";
42458                 this.elementStyle = "padding-left:0;";
42459             }
42460         }
42461         var stack = this.stack;
42462         var slen = stack.length;
42463         if(slen > 0){
42464             if(!this.fieldTpl){
42465                 var t = new Roo.Template(
42466                     '<div class="x-form-item {5}">',
42467                         '<label for="{0}" style="{2}">{1}{4}</label>',
42468                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42469                         '</div>',
42470                     '</div><div class="x-form-clear-left"></div>'
42471                 );
42472                 t.disableFormats = true;
42473                 t.compile();
42474                 Roo.form.Layout.prototype.fieldTpl = t;
42475             }
42476             for(var i = 0; i < slen; i++) {
42477                 if(stack[i].isFormField){
42478                     this.renderField(stack[i]);
42479                 }else{
42480                     this.renderComponent(stack[i]);
42481                 }
42482             }
42483         }
42484         if(this.clear){
42485             this.el.createChild({cls:'x-form-clear'});
42486         }
42487     },
42488
42489     // private
42490     renderField : function(f){
42491         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42492                f.id, //0
42493                f.fieldLabel, //1
42494                f.labelStyle||this.labelStyle||'', //2
42495                this.elementStyle||'', //3
42496                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42497                f.itemCls||this.itemCls||''  //5
42498        ], true).getPrevSibling());
42499     },
42500
42501     // private
42502     renderComponent : function(c){
42503         c.render(c.isLayout ? this.el : this.el.createChild());    
42504     },
42505     /**
42506      * Adds a object form elements (using the xtype property as the factory method.)
42507      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42508      * @param {Object} config 
42509      */
42510     addxtype : function(o)
42511     {
42512         // create the lement.
42513         o.form = this.form;
42514         var fe = Roo.factory(o, Roo.form);
42515         this.form.allItems.push(fe);
42516         this.stack.push(fe);
42517         
42518         if (fe.isFormField) {
42519             this.form.items.add(fe);
42520         }
42521          
42522         return fe;
42523     }
42524 });
42525
42526 /**
42527  * @class Roo.form.Column
42528  * @extends Roo.form.Layout
42529  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42530  * @constructor
42531  * @param {Object} config Configuration options
42532  */
42533 Roo.form.Column = function(config){
42534     Roo.form.Column.superclass.constructor.call(this, config);
42535 };
42536
42537 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42538     /**
42539      * @cfg {Number/String} width
42540      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42541      */
42542     /**
42543      * @cfg {String/Object} autoCreate
42544      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42545      */
42546
42547     // private
42548     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42549
42550     // private
42551     onRender : function(ct, position){
42552         Roo.form.Column.superclass.onRender.call(this, ct, position);
42553         if(this.width){
42554             this.el.setWidth(this.width);
42555         }
42556     }
42557 });
42558
42559
42560 /**
42561  * @class Roo.form.Row
42562  * @extends Roo.form.Layout
42563  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42564  * @constructor
42565  * @param {Object} config Configuration options
42566  */
42567
42568  
42569 Roo.form.Row = function(config){
42570     Roo.form.Row.superclass.constructor.call(this, config);
42571 };
42572  
42573 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42574       /**
42575      * @cfg {Number/String} width
42576      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42577      */
42578     /**
42579      * @cfg {Number/String} height
42580      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42581      */
42582     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42583     
42584     padWidth : 20,
42585     // private
42586     onRender : function(ct, position){
42587         //console.log('row render');
42588         if(!this.rowTpl){
42589             var t = new Roo.Template(
42590                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42591                     '<label for="{0}" style="{2}">{1}{4}</label>',
42592                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42593                     '</div>',
42594                 '</div>'
42595             );
42596             t.disableFormats = true;
42597             t.compile();
42598             Roo.form.Layout.prototype.rowTpl = t;
42599         }
42600         this.fieldTpl = this.rowTpl;
42601         
42602         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42603         var labelWidth = 100;
42604         
42605         if ((this.labelAlign != 'top')) {
42606             if (typeof this.labelWidth == 'number') {
42607                 labelWidth = this.labelWidth
42608             }
42609             this.padWidth =  20 + labelWidth;
42610             
42611         }
42612         
42613         Roo.form.Column.superclass.onRender.call(this, ct, position);
42614         if(this.width){
42615             this.el.setWidth(this.width);
42616         }
42617         if(this.height){
42618             this.el.setHeight(this.height);
42619         }
42620     },
42621     
42622     // private
42623     renderField : function(f){
42624         f.fieldEl = this.fieldTpl.append(this.el, [
42625                f.id, f.fieldLabel,
42626                f.labelStyle||this.labelStyle||'',
42627                this.elementStyle||'',
42628                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42629                f.itemCls||this.itemCls||'',
42630                f.width ? f.width + this.padWidth : 160 + this.padWidth
42631        ],true);
42632     }
42633 });
42634  
42635
42636 /**
42637  * @class Roo.form.FieldSet
42638  * @extends Roo.form.Layout
42639  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42640  * @constructor
42641  * @param {Object} config Configuration options
42642  */
42643 Roo.form.FieldSet = function(config){
42644     Roo.form.FieldSet.superclass.constructor.call(this, config);
42645 };
42646
42647 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42648     /**
42649      * @cfg {String} legend
42650      * The text to display as the legend for the FieldSet (defaults to '')
42651      */
42652     /**
42653      * @cfg {String/Object} autoCreate
42654      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42655      */
42656
42657     // private
42658     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42659
42660     // private
42661     onRender : function(ct, position){
42662         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42663         if(this.legend){
42664             this.setLegend(this.legend);
42665         }
42666     },
42667
42668     // private
42669     setLegend : function(text){
42670         if(this.rendered){
42671             this.el.child('legend').update(text);
42672         }
42673     }
42674 });/*
42675  * Based on:
42676  * Ext JS Library 1.1.1
42677  * Copyright(c) 2006-2007, Ext JS, LLC.
42678  *
42679  * Originally Released Under LGPL - original licence link has changed is not relivant.
42680  *
42681  * Fork - LGPL
42682  * <script type="text/javascript">
42683  */
42684 /**
42685  * @class Roo.form.VTypes
42686  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42687  * @singleton
42688  */
42689 Roo.form.VTypes = function(){
42690     // closure these in so they are only created once.
42691     var alpha = /^[a-zA-Z_]+$/;
42692     var alphanum = /^[a-zA-Z0-9_]+$/;
42693     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42694     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42695
42696     // All these messages and functions are configurable
42697     return {
42698         /**
42699          * The function used to validate email addresses
42700          * @param {String} value The email address
42701          */
42702         'email' : function(v){
42703             return email.test(v);
42704         },
42705         /**
42706          * The error text to display when the email validation function returns false
42707          * @type String
42708          */
42709         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42710         /**
42711          * The keystroke filter mask to be applied on email input
42712          * @type RegExp
42713          */
42714         'emailMask' : /[a-z0-9_\.\-@]/i,
42715
42716         /**
42717          * The function used to validate URLs
42718          * @param {String} value The URL
42719          */
42720         'url' : function(v){
42721             return url.test(v);
42722         },
42723         /**
42724          * The error text to display when the url validation function returns false
42725          * @type String
42726          */
42727         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42728         
42729         /**
42730          * The function used to validate alpha values
42731          * @param {String} value The value
42732          */
42733         'alpha' : function(v){
42734             return alpha.test(v);
42735         },
42736         /**
42737          * The error text to display when the alpha validation function returns false
42738          * @type String
42739          */
42740         'alphaText' : 'This field should only contain letters and _',
42741         /**
42742          * The keystroke filter mask to be applied on alpha input
42743          * @type RegExp
42744          */
42745         'alphaMask' : /[a-z_]/i,
42746
42747         /**
42748          * The function used to validate alphanumeric values
42749          * @param {String} value The value
42750          */
42751         'alphanum' : function(v){
42752             return alphanum.test(v);
42753         },
42754         /**
42755          * The error text to display when the alphanumeric validation function returns false
42756          * @type String
42757          */
42758         'alphanumText' : 'This field should only contain letters, numbers and _',
42759         /**
42760          * The keystroke filter mask to be applied on alphanumeric input
42761          * @type RegExp
42762          */
42763         'alphanumMask' : /[a-z0-9_]/i
42764     };
42765 }();//<script type="text/javascript">
42766
42767 /**
42768  * @class Roo.form.FCKeditor
42769  * @extends Roo.form.TextArea
42770  * Wrapper around the FCKEditor http://www.fckeditor.net
42771  * @constructor
42772  * Creates a new FCKeditor
42773  * @param {Object} config Configuration options
42774  */
42775 Roo.form.FCKeditor = function(config){
42776     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42777     this.addEvents({
42778          /**
42779          * @event editorinit
42780          * Fired when the editor is initialized - you can add extra handlers here..
42781          * @param {FCKeditor} this
42782          * @param {Object} the FCK object.
42783          */
42784         editorinit : true
42785     });
42786     
42787     
42788 };
42789 Roo.form.FCKeditor.editors = { };
42790 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42791 {
42792     //defaultAutoCreate : {
42793     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42794     //},
42795     // private
42796     /**
42797      * @cfg {Object} fck options - see fck manual for details.
42798      */
42799     fckconfig : false,
42800     
42801     /**
42802      * @cfg {Object} fck toolbar set (Basic or Default)
42803      */
42804     toolbarSet : 'Basic',
42805     /**
42806      * @cfg {Object} fck BasePath
42807      */ 
42808     basePath : '/fckeditor/',
42809     
42810     
42811     frame : false,
42812     
42813     value : '',
42814     
42815    
42816     onRender : function(ct, position)
42817     {
42818         if(!this.el){
42819             this.defaultAutoCreate = {
42820                 tag: "textarea",
42821                 style:"width:300px;height:60px;",
42822                 autocomplete: "off"
42823             };
42824         }
42825         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42826         /*
42827         if(this.grow){
42828             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42829             if(this.preventScrollbars){
42830                 this.el.setStyle("overflow", "hidden");
42831             }
42832             this.el.setHeight(this.growMin);
42833         }
42834         */
42835         //console.log('onrender' + this.getId() );
42836         Roo.form.FCKeditor.editors[this.getId()] = this;
42837          
42838
42839         this.replaceTextarea() ;
42840         
42841     },
42842     
42843     getEditor : function() {
42844         return this.fckEditor;
42845     },
42846     /**
42847      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42848      * @param {Mixed} value The value to set
42849      */
42850     
42851     
42852     setValue : function(value)
42853     {
42854         //console.log('setValue: ' + value);
42855         
42856         if(typeof(value) == 'undefined') { // not sure why this is happending...
42857             return;
42858         }
42859         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42860         
42861         //if(!this.el || !this.getEditor()) {
42862         //    this.value = value;
42863             //this.setValue.defer(100,this,[value]);    
42864         //    return;
42865         //} 
42866         
42867         if(!this.getEditor()) {
42868             return;
42869         }
42870         
42871         this.getEditor().SetData(value);
42872         
42873         //
42874
42875     },
42876
42877     /**
42878      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42879      * @return {Mixed} value The field value
42880      */
42881     getValue : function()
42882     {
42883         
42884         if (this.frame && this.frame.dom.style.display == 'none') {
42885             return Roo.form.FCKeditor.superclass.getValue.call(this);
42886         }
42887         
42888         if(!this.el || !this.getEditor()) {
42889            
42890            // this.getValue.defer(100,this); 
42891             return this.value;
42892         }
42893        
42894         
42895         var value=this.getEditor().GetData();
42896         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42897         return Roo.form.FCKeditor.superclass.getValue.call(this);
42898         
42899
42900     },
42901
42902     /**
42903      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42904      * @return {Mixed} value The field value
42905      */
42906     getRawValue : function()
42907     {
42908         if (this.frame && this.frame.dom.style.display == 'none') {
42909             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42910         }
42911         
42912         if(!this.el || !this.getEditor()) {
42913             //this.getRawValue.defer(100,this); 
42914             return this.value;
42915             return;
42916         }
42917         
42918         
42919         
42920         var value=this.getEditor().GetData();
42921         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42922         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42923          
42924     },
42925     
42926     setSize : function(w,h) {
42927         
42928         
42929         
42930         //if (this.frame && this.frame.dom.style.display == 'none') {
42931         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42932         //    return;
42933         //}
42934         //if(!this.el || !this.getEditor()) {
42935         //    this.setSize.defer(100,this, [w,h]); 
42936         //    return;
42937         //}
42938         
42939         
42940         
42941         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42942         
42943         this.frame.dom.setAttribute('width', w);
42944         this.frame.dom.setAttribute('height', h);
42945         this.frame.setSize(w,h);
42946         
42947     },
42948     
42949     toggleSourceEdit : function(value) {
42950         
42951       
42952          
42953         this.el.dom.style.display = value ? '' : 'none';
42954         this.frame.dom.style.display = value ?  'none' : '';
42955         
42956     },
42957     
42958     
42959     focus: function(tag)
42960     {
42961         if (this.frame.dom.style.display == 'none') {
42962             return Roo.form.FCKeditor.superclass.focus.call(this);
42963         }
42964         if(!this.el || !this.getEditor()) {
42965             this.focus.defer(100,this, [tag]); 
42966             return;
42967         }
42968         
42969         
42970         
42971         
42972         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42973         this.getEditor().Focus();
42974         if (tgs.length) {
42975             if (!this.getEditor().Selection.GetSelection()) {
42976                 this.focus.defer(100,this, [tag]); 
42977                 return;
42978             }
42979             
42980             
42981             var r = this.getEditor().EditorDocument.createRange();
42982             r.setStart(tgs[0],0);
42983             r.setEnd(tgs[0],0);
42984             this.getEditor().Selection.GetSelection().removeAllRanges();
42985             this.getEditor().Selection.GetSelection().addRange(r);
42986             this.getEditor().Focus();
42987         }
42988         
42989     },
42990     
42991     
42992     
42993     replaceTextarea : function()
42994     {
42995         if ( document.getElementById( this.getId() + '___Frame' ) )
42996             return ;
42997         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42998         //{
42999             // We must check the elements firstly using the Id and then the name.
43000         var oTextarea = document.getElementById( this.getId() );
43001         
43002         var colElementsByName = document.getElementsByName( this.getId() ) ;
43003          
43004         oTextarea.style.display = 'none' ;
43005
43006         if ( oTextarea.tabIndex ) {            
43007             this.TabIndex = oTextarea.tabIndex ;
43008         }
43009         
43010         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43011         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43012         this.frame = Roo.get(this.getId() + '___Frame')
43013     },
43014     
43015     _getConfigHtml : function()
43016     {
43017         var sConfig = '' ;
43018
43019         for ( var o in this.fckconfig ) {
43020             sConfig += sConfig.length > 0  ? '&amp;' : '';
43021             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43022         }
43023
43024         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43025     },
43026     
43027     
43028     _getIFrameHtml : function()
43029     {
43030         var sFile = 'fckeditor.html' ;
43031         /* no idea what this is about..
43032         try
43033         {
43034             if ( (/fcksource=true/i).test( window.top.location.search ) )
43035                 sFile = 'fckeditor.original.html' ;
43036         }
43037         catch (e) { 
43038         */
43039
43040         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43041         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43042         
43043         
43044         var html = '<iframe id="' + this.getId() +
43045             '___Frame" src="' + sLink +
43046             '" width="' + this.width +
43047             '" height="' + this.height + '"' +
43048             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43049             ' frameborder="0" scrolling="no"></iframe>' ;
43050
43051         return html ;
43052     },
43053     
43054     _insertHtmlBefore : function( html, element )
43055     {
43056         if ( element.insertAdjacentHTML )       {
43057             // IE
43058             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43059         } else { // Gecko
43060             var oRange = document.createRange() ;
43061             oRange.setStartBefore( element ) ;
43062             var oFragment = oRange.createContextualFragment( html );
43063             element.parentNode.insertBefore( oFragment, element ) ;
43064         }
43065     }
43066     
43067     
43068   
43069     
43070     
43071     
43072     
43073
43074 });
43075
43076 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43077
43078 function FCKeditor_OnComplete(editorInstance){
43079     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43080     f.fckEditor = editorInstance;
43081     //console.log("loaded");
43082     f.fireEvent('editorinit', f, editorInstance);
43083
43084   
43085
43086  
43087
43088
43089
43090
43091
43092
43093
43094
43095
43096
43097
43098
43099
43100
43101
43102 //<script type="text/javascript">
43103 /**
43104  * @class Roo.form.GridField
43105  * @extends Roo.form.Field
43106  * Embed a grid (or editable grid into a form)
43107  * STATUS ALPHA
43108  * 
43109  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43110  * it needs 
43111  * xgrid.store = Roo.data.Store
43112  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43113  * xgrid.store.reader = Roo.data.JsonReader 
43114  * 
43115  * 
43116  * @constructor
43117  * Creates a new GridField
43118  * @param {Object} config Configuration options
43119  */
43120 Roo.form.GridField = function(config){
43121     Roo.form.GridField.superclass.constructor.call(this, config);
43122      
43123 };
43124
43125 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43126     /**
43127      * @cfg {Number} width  - used to restrict width of grid..
43128      */
43129     width : 100,
43130     /**
43131      * @cfg {Number} height - used to restrict height of grid..
43132      */
43133     height : 50,
43134      /**
43135      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43136          * 
43137          *}
43138      */
43139     xgrid : false, 
43140     /**
43141      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43142      * {tag: "input", type: "checkbox", autocomplete: "off"})
43143      */
43144    // defaultAutoCreate : { tag: 'div' },
43145     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43146     /**
43147      * @cfg {String} addTitle Text to include for adding a title.
43148      */
43149     addTitle : false,
43150     //
43151     onResize : function(){
43152         Roo.form.Field.superclass.onResize.apply(this, arguments);
43153     },
43154
43155     initEvents : function(){
43156         // Roo.form.Checkbox.superclass.initEvents.call(this);
43157         // has no events...
43158        
43159     },
43160
43161
43162     getResizeEl : function(){
43163         return this.wrap;
43164     },
43165
43166     getPositionEl : function(){
43167         return this.wrap;
43168     },
43169
43170     // private
43171     onRender : function(ct, position){
43172         
43173         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43174         var style = this.style;
43175         delete this.style;
43176         
43177         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43178         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43179         this.viewEl = this.wrap.createChild({ tag: 'div' });
43180         if (style) {
43181             this.viewEl.applyStyles(style);
43182         }
43183         if (this.width) {
43184             this.viewEl.setWidth(this.width);
43185         }
43186         if (this.height) {
43187             this.viewEl.setHeight(this.height);
43188         }
43189         //if(this.inputValue !== undefined){
43190         //this.setValue(this.value);
43191         
43192         
43193         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43194         
43195         
43196         this.grid.render();
43197         this.grid.getDataSource().on('remove', this.refreshValue, this);
43198         this.grid.getDataSource().on('update', this.refreshValue, this);
43199         this.grid.on('afteredit', this.refreshValue, this);
43200  
43201     },
43202      
43203     
43204     /**
43205      * Sets the value of the item. 
43206      * @param {String} either an object  or a string..
43207      */
43208     setValue : function(v){
43209         //this.value = v;
43210         v = v || []; // empty set..
43211         // this does not seem smart - it really only affects memoryproxy grids..
43212         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43213             var ds = this.grid.getDataSource();
43214             // assumes a json reader..
43215             var data = {}
43216             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43217             ds.loadData( data);
43218         }
43219         // clear selection so it does not get stale.
43220         if (this.grid.sm) { 
43221             this.grid.sm.clearSelections();
43222         }
43223         
43224         Roo.form.GridField.superclass.setValue.call(this, v);
43225         this.refreshValue();
43226         // should load data in the grid really....
43227     },
43228     
43229     // private
43230     refreshValue: function() {
43231          var val = [];
43232         this.grid.getDataSource().each(function(r) {
43233             val.push(r.data);
43234         });
43235         this.el.dom.value = Roo.encode(val);
43236     }
43237     
43238      
43239     
43240     
43241 });/*
43242  * Based on:
43243  * Ext JS Library 1.1.1
43244  * Copyright(c) 2006-2007, Ext JS, LLC.
43245  *
43246  * Originally Released Under LGPL - original licence link has changed is not relivant.
43247  *
43248  * Fork - LGPL
43249  * <script type="text/javascript">
43250  */
43251 /**
43252  * @class Roo.form.DisplayField
43253  * @extends Roo.form.Field
43254  * A generic Field to display non-editable data.
43255  * @constructor
43256  * Creates a new Display Field item.
43257  * @param {Object} config Configuration options
43258  */
43259 Roo.form.DisplayField = function(config){
43260     Roo.form.DisplayField.superclass.constructor.call(this, config);
43261     
43262 };
43263
43264 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43265     inputType:      'hidden',
43266     allowBlank:     true,
43267     readOnly:         true,
43268     
43269  
43270     /**
43271      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43272      */
43273     focusClass : undefined,
43274     /**
43275      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43276      */
43277     fieldClass: 'x-form-field',
43278     
43279      /**
43280      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43281      */
43282     valueRenderer: undefined,
43283     
43284     width: 100,
43285     /**
43286      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43287      * {tag: "input", type: "checkbox", autocomplete: "off"})
43288      */
43289      
43290  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43291
43292     onResize : function(){
43293         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43294         
43295     },
43296
43297     initEvents : function(){
43298         // Roo.form.Checkbox.superclass.initEvents.call(this);
43299         // has no events...
43300        
43301     },
43302
43303
43304     getResizeEl : function(){
43305         return this.wrap;
43306     },
43307
43308     getPositionEl : function(){
43309         return this.wrap;
43310     },
43311
43312     // private
43313     onRender : function(ct, position){
43314         
43315         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43316         //if(this.inputValue !== undefined){
43317         this.wrap = this.el.wrap();
43318         
43319         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43320         
43321         if (this.bodyStyle) {
43322             this.viewEl.applyStyles(this.bodyStyle);
43323         }
43324         //this.viewEl.setStyle('padding', '2px');
43325         
43326         this.setValue(this.value);
43327         
43328     },
43329 /*
43330     // private
43331     initValue : Roo.emptyFn,
43332
43333   */
43334
43335         // private
43336     onClick : function(){
43337         
43338     },
43339
43340     /**
43341      * Sets the checked state of the checkbox.
43342      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43343      */
43344     setValue : function(v){
43345         this.value = v;
43346         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43347         // this might be called before we have a dom element..
43348         if (!this.viewEl) {
43349             return;
43350         }
43351         this.viewEl.dom.innerHTML = html;
43352         Roo.form.DisplayField.superclass.setValue.call(this, v);
43353
43354     }
43355 });/*
43356  * 
43357  * Licence- LGPL
43358  * 
43359  */
43360
43361 /**
43362  * @class Roo.form.DayPicker
43363  * @extends Roo.form.Field
43364  * A Day picker show [M] [T] [W] ....
43365  * @constructor
43366  * Creates a new Day Picker
43367  * @param {Object} config Configuration options
43368  */
43369 Roo.form.DayPicker= function(config){
43370     Roo.form.DayPicker.superclass.constructor.call(this, config);
43371      
43372 };
43373
43374 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43375     /**
43376      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43377      */
43378     focusClass : undefined,
43379     /**
43380      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43381      */
43382     fieldClass: "x-form-field",
43383    
43384     /**
43385      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43386      * {tag: "input", type: "checkbox", autocomplete: "off"})
43387      */
43388     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43389     
43390    
43391     actionMode : 'viewEl', 
43392     //
43393     // private
43394  
43395     inputType : 'hidden',
43396     
43397      
43398     inputElement: false, // real input element?
43399     basedOn: false, // ????
43400     
43401     isFormField: true, // not sure where this is needed!!!!
43402
43403     onResize : function(){
43404         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43405         if(!this.boxLabel){
43406             this.el.alignTo(this.wrap, 'c-c');
43407         }
43408     },
43409
43410     initEvents : function(){
43411         Roo.form.Checkbox.superclass.initEvents.call(this);
43412         this.el.on("click", this.onClick,  this);
43413         this.el.on("change", this.onClick,  this);
43414     },
43415
43416
43417     getResizeEl : function(){
43418         return this.wrap;
43419     },
43420
43421     getPositionEl : function(){
43422         return this.wrap;
43423     },
43424
43425     
43426     // private
43427     onRender : function(ct, position){
43428         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43429        
43430         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43431         
43432         var r1 = '<table><tr>';
43433         var r2 = '<tr class="x-form-daypick-icons">';
43434         for (var i=0; i < 7; i++) {
43435             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43436             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43437         }
43438         
43439         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43440         viewEl.select('img').on('click', this.onClick, this);
43441         this.viewEl = viewEl;   
43442         
43443         
43444         // this will not work on Chrome!!!
43445         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43446         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43447         
43448         
43449           
43450
43451     },
43452
43453     // private
43454     initValue : Roo.emptyFn,
43455
43456     /**
43457      * Returns the checked state of the checkbox.
43458      * @return {Boolean} True if checked, else false
43459      */
43460     getValue : function(){
43461         return this.el.dom.value;
43462         
43463     },
43464
43465         // private
43466     onClick : function(e){ 
43467         //this.setChecked(!this.checked);
43468         Roo.get(e.target).toggleClass('x-menu-item-checked');
43469         this.refreshValue();
43470         //if(this.el.dom.checked != this.checked){
43471         //    this.setValue(this.el.dom.checked);
43472        // }
43473     },
43474     
43475     // private
43476     refreshValue : function()
43477     {
43478         var val = '';
43479         this.viewEl.select('img',true).each(function(e,i,n)  {
43480             val += e.is(".x-menu-item-checked") ? String(n) : '';
43481         });
43482         this.setValue(val, true);
43483     },
43484
43485     /**
43486      * Sets the checked state of the checkbox.
43487      * On is always based on a string comparison between inputValue and the param.
43488      * @param {Boolean/String} value - the value to set 
43489      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43490      */
43491     setValue : function(v,suppressEvent){
43492         if (!this.el.dom) {
43493             return;
43494         }
43495         var old = this.el.dom.value ;
43496         this.el.dom.value = v;
43497         if (suppressEvent) {
43498             return ;
43499         }
43500          
43501         // update display..
43502         this.viewEl.select('img',true).each(function(e,i,n)  {
43503             
43504             var on = e.is(".x-menu-item-checked");
43505             var newv = v.indexOf(String(n)) > -1;
43506             if (on != newv) {
43507                 e.toggleClass('x-menu-item-checked');
43508             }
43509             
43510         });
43511         
43512         
43513         this.fireEvent('change', this, v, old);
43514         
43515         
43516     },
43517    
43518     // handle setting of hidden value by some other method!!?!?
43519     setFromHidden: function()
43520     {
43521         if(!this.el){
43522             return;
43523         }
43524         //console.log("SET FROM HIDDEN");
43525         //alert('setFrom hidden');
43526         this.setValue(this.el.dom.value);
43527     },
43528     
43529     onDestroy : function()
43530     {
43531         if(this.viewEl){
43532             Roo.get(this.viewEl).remove();
43533         }
43534          
43535         Roo.form.DayPicker.superclass.onDestroy.call(this);
43536     }
43537
43538 });/*
43539  * RooJS Library 1.1.1
43540  * Copyright(c) 2008-2011  Alan Knowles
43541  *
43542  * License - LGPL
43543  */
43544  
43545
43546 /**
43547  * @class Roo.form.ComboCheck
43548  * @extends Roo.form.ComboBox
43549  * A combobox for multiple select items.
43550  *
43551  * FIXME - could do with a reset button..
43552  * 
43553  * @constructor
43554  * Create a new ComboCheck
43555  * @param {Object} config Configuration options
43556  */
43557 Roo.form.ComboCheck = function(config){
43558     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43559     // should verify some data...
43560     // like
43561     // hiddenName = required..
43562     // displayField = required
43563     // valudField == required
43564     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43565     var _t = this;
43566     Roo.each(req, function(e) {
43567         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43568             throw "Roo.form.ComboCheck : missing value for: " + e;
43569         }
43570     });
43571     
43572     
43573 };
43574
43575 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43576      
43577      
43578     editable : false,
43579      
43580     selectedClass: 'x-menu-item-checked', 
43581     
43582     // private
43583     onRender : function(ct, position){
43584         var _t = this;
43585         
43586         
43587         
43588         if(!this.tpl){
43589             var cls = 'x-combo-list';
43590
43591             
43592             this.tpl =  new Roo.Template({
43593                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43594                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43595                    '<span>{' + this.displayField + '}</span>' +
43596                     '</div>' 
43597                 
43598             });
43599         }
43600  
43601         
43602         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43603         this.view.singleSelect = false;
43604         this.view.multiSelect = true;
43605         this.view.toggleSelect = true;
43606         this.pageTb.add(new Roo.Toolbar.Fill(), {
43607             
43608             text: 'Done',
43609             handler: function()
43610             {
43611                 _t.collapse();
43612             }
43613         });
43614     },
43615     
43616     onViewOver : function(e, t){
43617         // do nothing...
43618         return;
43619         
43620     },
43621     
43622     onViewClick : function(doFocus,index){
43623         return;
43624         
43625     },
43626     select: function () {
43627         //Roo.log("SELECT CALLED");
43628     },
43629      
43630     selectByValue : function(xv, scrollIntoView){
43631         var ar = this.getValueArray();
43632         var sels = [];
43633         
43634         Roo.each(ar, function(v) {
43635             if(v === undefined || v === null){
43636                 return;
43637             }
43638             var r = this.findRecord(this.valueField, v);
43639             if(r){
43640                 sels.push(this.store.indexOf(r))
43641                 
43642             }
43643         },this);
43644         this.view.select(sels);
43645         return false;
43646     },
43647     
43648     
43649     
43650     onSelect : function(record, index){
43651        // Roo.log("onselect Called");
43652        // this is only called by the clear button now..
43653         this.view.clearSelections();
43654         this.setValue('[]');
43655         if (this.value != this.valueBefore) {
43656             this.fireEvent('change', this, this.value, this.valueBefore);
43657         }
43658     },
43659     getValueArray : function()
43660     {
43661         var ar = [] ;
43662         
43663         try {
43664             Roo.log(this.value);
43665             var ar = Roo.decode(this.value);
43666             return  ar instanceof Array ? ar : []; //?? valid?
43667             
43668         } catch(e) {
43669             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43670             return [];
43671         }
43672          
43673     },
43674     expand : function ()
43675     {
43676         Roo.form.ComboCheck.superclass.expand.call(this);
43677         this.valueBefore = this.value;
43678         
43679
43680     },
43681     
43682     collapse : function(){
43683         Roo.form.ComboCheck.superclass.collapse.call(this);
43684         var sl = this.view.getSelectedIndexes();
43685         var st = this.store;
43686         var nv = [];
43687         var tv = [];
43688         var r;
43689         Roo.each(sl, function(i) {
43690             r = st.getAt(i);
43691             nv.push(r.get(this.valueField));
43692         },this);
43693         this.setValue(Roo.encode(nv));
43694         if (this.value != this.valueBefore) {
43695
43696             this.fireEvent('change', this, this.value, this.valueBefore);
43697         }
43698         
43699     },
43700     
43701     setValue : function(v){
43702         // Roo.log(v);
43703         this.value = v;
43704         
43705         var vals = this.getValueArray();
43706         var tv = [];
43707         Roo.each(vals, function(k) {
43708             var r = this.findRecord(this.valueField, k);
43709             if(r){
43710                 tv.push(r.data[this.displayField]);
43711             }else if(this.valueNotFoundText !== undefined){
43712                 tv.push( this.valueNotFoundText );
43713             }
43714         },this);
43715        // Roo.log(tv);
43716         
43717         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43718         this.hiddenField.value = v;
43719         this.value = v;
43720     }
43721     
43722 });//<script type="text/javasscript">
43723  
43724
43725 /**
43726  * @class Roo.DDView
43727  * A DnD enabled version of Roo.View.
43728  * @param {Element/String} container The Element in which to create the View.
43729  * @param {String} tpl The template string used to create the markup for each element of the View
43730  * @param {Object} config The configuration properties. These include all the config options of
43731  * {@link Roo.View} plus some specific to this class.<br>
43732  * <p>
43733  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43734  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43735  * <p>
43736  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43737 .x-view-drag-insert-above {
43738         border-top:1px dotted #3366cc;
43739 }
43740 .x-view-drag-insert-below {
43741         border-bottom:1px dotted #3366cc;
43742 }
43743 </code></pre>
43744  * 
43745  */
43746  
43747 Roo.DDView = function(container, tpl, config) {
43748     Roo.DDView.superclass.constructor.apply(this, arguments);
43749     this.getEl().setStyle("outline", "0px none");
43750     this.getEl().unselectable();
43751     if (this.dragGroup) {
43752                 this.setDraggable(this.dragGroup.split(","));
43753     }
43754     if (this.dropGroup) {
43755                 this.setDroppable(this.dropGroup.split(","));
43756     }
43757     if (this.deletable) {
43758         this.setDeletable();
43759     }
43760     this.isDirtyFlag = false;
43761         this.addEvents({
43762                 "drop" : true
43763         });
43764 };
43765
43766 Roo.extend(Roo.DDView, Roo.View, {
43767 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43768 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43769 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43770 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43771
43772         isFormField: true,
43773
43774         reset: Roo.emptyFn,
43775         
43776         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43777
43778         validate: function() {
43779                 return true;
43780         },
43781         
43782         destroy: function() {
43783                 this.purgeListeners();
43784                 this.getEl.removeAllListeners();
43785                 this.getEl().remove();
43786                 if (this.dragZone) {
43787                         if (this.dragZone.destroy) {
43788                                 this.dragZone.destroy();
43789                         }
43790                 }
43791                 if (this.dropZone) {
43792                         if (this.dropZone.destroy) {
43793                                 this.dropZone.destroy();
43794                         }
43795                 }
43796         },
43797
43798 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43799         getName: function() {
43800                 return this.name;
43801         },
43802
43803 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43804         setValue: function(v) {
43805                 if (!this.store) {
43806                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43807                 }
43808                 var data = {};
43809                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43810                 this.store.proxy = new Roo.data.MemoryProxy(data);
43811                 this.store.load();
43812         },
43813
43814 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43815         getValue: function() {
43816                 var result = '(';
43817                 this.store.each(function(rec) {
43818                         result += rec.id + ',';
43819                 });
43820                 return result.substr(0, result.length - 1) + ')';
43821         },
43822         
43823         getIds: function() {
43824                 var i = 0, result = new Array(this.store.getCount());
43825                 this.store.each(function(rec) {
43826                         result[i++] = rec.id;
43827                 });
43828                 return result;
43829         },
43830         
43831         isDirty: function() {
43832                 return this.isDirtyFlag;
43833         },
43834
43835 /**
43836  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43837  *      whole Element becomes the target, and this causes the drop gesture to append.
43838  */
43839     getTargetFromEvent : function(e) {
43840                 var target = e.getTarget();
43841                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43842                 target = target.parentNode;
43843                 }
43844                 if (!target) {
43845                         target = this.el.dom.lastChild || this.el.dom;
43846                 }
43847                 return target;
43848     },
43849
43850 /**
43851  *      Create the drag data which consists of an object which has the property "ddel" as
43852  *      the drag proxy element. 
43853  */
43854     getDragData : function(e) {
43855         var target = this.findItemFromChild(e.getTarget());
43856                 if(target) {
43857                         this.handleSelection(e);
43858                         var selNodes = this.getSelectedNodes();
43859             var dragData = {
43860                 source: this,
43861                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43862                 nodes: selNodes,
43863                 records: []
43864                         };
43865                         var selectedIndices = this.getSelectedIndexes();
43866                         for (var i = 0; i < selectedIndices.length; i++) {
43867                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43868                         }
43869                         if (selNodes.length == 1) {
43870                                 dragData.ddel = target.cloneNode(true); // the div element
43871                         } else {
43872                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43873                                 div.className = 'multi-proxy';
43874                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43875                                         div.appendChild(selNodes[i].cloneNode(true));
43876                                 }
43877                                 dragData.ddel = div;
43878                         }
43879             //console.log(dragData)
43880             //console.log(dragData.ddel.innerHTML)
43881                         return dragData;
43882                 }
43883         //console.log('nodragData')
43884                 return false;
43885     },
43886     
43887 /**     Specify to which ddGroup items in this DDView may be dragged. */
43888     setDraggable: function(ddGroup) {
43889         if (ddGroup instanceof Array) {
43890                 Roo.each(ddGroup, this.setDraggable, this);
43891                 return;
43892         }
43893         if (this.dragZone) {
43894                 this.dragZone.addToGroup(ddGroup);
43895         } else {
43896                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43897                                 containerScroll: true,
43898                                 ddGroup: ddGroup 
43899
43900                         });
43901 //                      Draggability implies selection. DragZone's mousedown selects the element.
43902                         if (!this.multiSelect) { this.singleSelect = true; }
43903
43904 //                      Wire the DragZone's handlers up to methods in *this*
43905                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43906                 }
43907     },
43908
43909 /**     Specify from which ddGroup this DDView accepts drops. */
43910     setDroppable: function(ddGroup) {
43911         if (ddGroup instanceof Array) {
43912                 Roo.each(ddGroup, this.setDroppable, this);
43913                 return;
43914         }
43915         if (this.dropZone) {
43916                 this.dropZone.addToGroup(ddGroup);
43917         } else {
43918                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43919                                 containerScroll: true,
43920                                 ddGroup: ddGroup
43921                         });
43922
43923 //                      Wire the DropZone's handlers up to methods in *this*
43924                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43925                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43926                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43927                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43928                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43929                 }
43930     },
43931
43932 /**     Decide whether to drop above or below a View node. */
43933     getDropPoint : function(e, n, dd){
43934         if (n == this.el.dom) { return "above"; }
43935                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43936                 var c = t + (b - t) / 2;
43937                 var y = Roo.lib.Event.getPageY(e);
43938                 if(y <= c) {
43939                         return "above";
43940                 }else{
43941                         return "below";
43942                 }
43943     },
43944
43945     onNodeEnter : function(n, dd, e, data){
43946                 return false;
43947     },
43948     
43949     onNodeOver : function(n, dd, e, data){
43950                 var pt = this.getDropPoint(e, n, dd);
43951                 // set the insert point style on the target node
43952                 var dragElClass = this.dropNotAllowed;
43953                 if (pt) {
43954                         var targetElClass;
43955                         if (pt == "above"){
43956                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43957                                 targetElClass = "x-view-drag-insert-above";
43958                         } else {
43959                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43960                                 targetElClass = "x-view-drag-insert-below";
43961                         }
43962                         if (this.lastInsertClass != targetElClass){
43963                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43964                                 this.lastInsertClass = targetElClass;
43965                         }
43966                 }
43967                 return dragElClass;
43968         },
43969
43970     onNodeOut : function(n, dd, e, data){
43971                 this.removeDropIndicators(n);
43972     },
43973
43974     onNodeDrop : function(n, dd, e, data){
43975         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43976                 return false;
43977         }
43978         var pt = this.getDropPoint(e, n, dd);
43979                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43980                 if (pt == "below") { insertAt++; }
43981                 for (var i = 0; i < data.records.length; i++) {
43982                         var r = data.records[i];
43983                         var dup = this.store.getById(r.id);
43984                         if (dup && (dd != this.dragZone)) {
43985                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43986                         } else {
43987                                 if (data.copy) {
43988                                         this.store.insert(insertAt++, r.copy());
43989                                 } else {
43990                                         data.source.isDirtyFlag = true;
43991                                         r.store.remove(r);
43992                                         this.store.insert(insertAt++, r);
43993                                 }
43994                                 this.isDirtyFlag = true;
43995                         }
43996                 }
43997                 this.dragZone.cachedTarget = null;
43998                 return true;
43999     },
44000
44001     removeDropIndicators : function(n){
44002                 if(n){
44003                         Roo.fly(n).removeClass([
44004                                 "x-view-drag-insert-above",
44005                                 "x-view-drag-insert-below"]);
44006                         this.lastInsertClass = "_noclass";
44007                 }
44008     },
44009
44010 /**
44011  *      Utility method. Add a delete option to the DDView's context menu.
44012  *      @param {String} imageUrl The URL of the "delete" icon image.
44013  */
44014         setDeletable: function(imageUrl) {
44015                 if (!this.singleSelect && !this.multiSelect) {
44016                         this.singleSelect = true;
44017                 }
44018                 var c = this.getContextMenu();
44019                 this.contextMenu.on("itemclick", function(item) {
44020                         switch (item.id) {
44021                                 case "delete":
44022                                         this.remove(this.getSelectedIndexes());
44023                                         break;
44024                         }
44025                 }, this);
44026                 this.contextMenu.add({
44027                         icon: imageUrl,
44028                         id: "delete",
44029                         text: 'Delete'
44030                 });
44031         },
44032         
44033 /**     Return the context menu for this DDView. */
44034         getContextMenu: function() {
44035                 if (!this.contextMenu) {
44036 //                      Create the View's context menu
44037                         this.contextMenu = new Roo.menu.Menu({
44038                                 id: this.id + "-contextmenu"
44039                         });
44040                         this.el.on("contextmenu", this.showContextMenu, this);
44041                 }
44042                 return this.contextMenu;
44043         },
44044         
44045         disableContextMenu: function() {
44046                 if (this.contextMenu) {
44047                         this.el.un("contextmenu", this.showContextMenu, this);
44048                 }
44049         },
44050
44051         showContextMenu: function(e, item) {
44052         item = this.findItemFromChild(e.getTarget());
44053                 if (item) {
44054                         e.stopEvent();
44055                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44056                         this.contextMenu.showAt(e.getXY());
44057             }
44058     },
44059
44060 /**
44061  *      Remove {@link Roo.data.Record}s at the specified indices.
44062  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44063  */
44064     remove: function(selectedIndices) {
44065                 selectedIndices = [].concat(selectedIndices);
44066                 for (var i = 0; i < selectedIndices.length; i++) {
44067                         var rec = this.store.getAt(selectedIndices[i]);
44068                         this.store.remove(rec);
44069                 }
44070     },
44071
44072 /**
44073  *      Double click fires the event, but also, if this is draggable, and there is only one other
44074  *      related DropZone, it transfers the selected node.
44075  */
44076     onDblClick : function(e){
44077         var item = this.findItemFromChild(e.getTarget());
44078         if(item){
44079             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44080                 return false;
44081             }
44082             if (this.dragGroup) {
44083                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44084                     while (targets.indexOf(this.dropZone) > -1) {
44085                             targets.remove(this.dropZone);
44086                                 }
44087                     if (targets.length == 1) {
44088                                         this.dragZone.cachedTarget = null;
44089                         var el = Roo.get(targets[0].getEl());
44090                         var box = el.getBox(true);
44091                         targets[0].onNodeDrop(el.dom, {
44092                                 target: el.dom,
44093                                 xy: [box.x, box.y + box.height - 1]
44094                         }, null, this.getDragData(e));
44095                     }
44096                 }
44097         }
44098     },
44099     
44100     handleSelection: function(e) {
44101                 this.dragZone.cachedTarget = null;
44102         var item = this.findItemFromChild(e.getTarget());
44103         if (!item) {
44104                 this.clearSelections(true);
44105                 return;
44106         }
44107                 if (item && (this.multiSelect || this.singleSelect)){
44108                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44109                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44110                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44111                                 this.unselect(item);
44112                         } else {
44113                                 this.select(item, this.multiSelect && e.ctrlKey);
44114                                 this.lastSelection = item;
44115                         }
44116                 }
44117     },
44118
44119     onItemClick : function(item, index, e){
44120                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44121                         return false;
44122                 }
44123                 return true;
44124     },
44125
44126     unselect : function(nodeInfo, suppressEvent){
44127                 var node = this.getNode(nodeInfo);
44128                 if(node && this.isSelected(node)){
44129                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44130                                 Roo.fly(node).removeClass(this.selectedClass);
44131                                 this.selections.remove(node);
44132                                 if(!suppressEvent){
44133                                         this.fireEvent("selectionchange", this, this.selections);
44134                                 }
44135                         }
44136                 }
44137     }
44138 });
44139 /*
44140  * Based on:
44141  * Ext JS Library 1.1.1
44142  * Copyright(c) 2006-2007, Ext JS, LLC.
44143  *
44144  * Originally Released Under LGPL - original licence link has changed is not relivant.
44145  *
44146  * Fork - LGPL
44147  * <script type="text/javascript">
44148  */
44149  
44150 /**
44151  * @class Roo.LayoutManager
44152  * @extends Roo.util.Observable
44153  * Base class for layout managers.
44154  */
44155 Roo.LayoutManager = function(container, config){
44156     Roo.LayoutManager.superclass.constructor.call(this);
44157     this.el = Roo.get(container);
44158     // ie scrollbar fix
44159     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44160         document.body.scroll = "no";
44161     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44162         this.el.position('relative');
44163     }
44164     this.id = this.el.id;
44165     this.el.addClass("x-layout-container");
44166     /** false to disable window resize monitoring @type Boolean */
44167     this.monitorWindowResize = true;
44168     this.regions = {};
44169     this.addEvents({
44170         /**
44171          * @event layout
44172          * Fires when a layout is performed. 
44173          * @param {Roo.LayoutManager} this
44174          */
44175         "layout" : true,
44176         /**
44177          * @event regionresized
44178          * Fires when the user resizes a region. 
44179          * @param {Roo.LayoutRegion} region The resized region
44180          * @param {Number} newSize The new size (width for east/west, height for north/south)
44181          */
44182         "regionresized" : true,
44183         /**
44184          * @event regioncollapsed
44185          * Fires when a region is collapsed. 
44186          * @param {Roo.LayoutRegion} region The collapsed region
44187          */
44188         "regioncollapsed" : true,
44189         /**
44190          * @event regionexpanded
44191          * Fires when a region is expanded.  
44192          * @param {Roo.LayoutRegion} region The expanded region
44193          */
44194         "regionexpanded" : true
44195     });
44196     this.updating = false;
44197     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44198 };
44199
44200 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44201     /**
44202      * Returns true if this layout is currently being updated
44203      * @return {Boolean}
44204      */
44205     isUpdating : function(){
44206         return this.updating; 
44207     },
44208     
44209     /**
44210      * Suspend the LayoutManager from doing auto-layouts while
44211      * making multiple add or remove calls
44212      */
44213     beginUpdate : function(){
44214         this.updating = true;    
44215     },
44216     
44217     /**
44218      * Restore auto-layouts and optionally disable the manager from performing a layout
44219      * @param {Boolean} noLayout true to disable a layout update 
44220      */
44221     endUpdate : function(noLayout){
44222         this.updating = false;
44223         if(!noLayout){
44224             this.layout();
44225         }    
44226     },
44227     
44228     layout: function(){
44229         
44230     },
44231     
44232     onRegionResized : function(region, newSize){
44233         this.fireEvent("regionresized", region, newSize);
44234         this.layout();
44235     },
44236     
44237     onRegionCollapsed : function(region){
44238         this.fireEvent("regioncollapsed", region);
44239     },
44240     
44241     onRegionExpanded : function(region){
44242         this.fireEvent("regionexpanded", region);
44243     },
44244         
44245     /**
44246      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44247      * performs box-model adjustments.
44248      * @return {Object} The size as an object {width: (the width), height: (the height)}
44249      */
44250     getViewSize : function(){
44251         var size;
44252         if(this.el.dom != document.body){
44253             size = this.el.getSize();
44254         }else{
44255             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44256         }
44257         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44258         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44259         return size;
44260     },
44261     
44262     /**
44263      * Returns the Element this layout is bound to.
44264      * @return {Roo.Element}
44265      */
44266     getEl : function(){
44267         return this.el;
44268     },
44269     
44270     /**
44271      * Returns the specified region.
44272      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44273      * @return {Roo.LayoutRegion}
44274      */
44275     getRegion : function(target){
44276         return this.regions[target.toLowerCase()];
44277     },
44278     
44279     onWindowResize : function(){
44280         if(this.monitorWindowResize){
44281             this.layout();
44282         }
44283     }
44284 });/*
44285  * Based on:
44286  * Ext JS Library 1.1.1
44287  * Copyright(c) 2006-2007, Ext JS, LLC.
44288  *
44289  * Originally Released Under LGPL - original licence link has changed is not relivant.
44290  *
44291  * Fork - LGPL
44292  * <script type="text/javascript">
44293  */
44294 /**
44295  * @class Roo.BorderLayout
44296  * @extends Roo.LayoutManager
44297  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44298  * please see: <br><br>
44299  * <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>
44300  * <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>
44301  * Example:
44302  <pre><code>
44303  var layout = new Roo.BorderLayout(document.body, {
44304     north: {
44305         initialSize: 25,
44306         titlebar: false
44307     },
44308     west: {
44309         split:true,
44310         initialSize: 200,
44311         minSize: 175,
44312         maxSize: 400,
44313         titlebar: true,
44314         collapsible: true
44315     },
44316     east: {
44317         split:true,
44318         initialSize: 202,
44319         minSize: 175,
44320         maxSize: 400,
44321         titlebar: true,
44322         collapsible: true
44323     },
44324     south: {
44325         split:true,
44326         initialSize: 100,
44327         minSize: 100,
44328         maxSize: 200,
44329         titlebar: true,
44330         collapsible: true
44331     },
44332     center: {
44333         titlebar: true,
44334         autoScroll:true,
44335         resizeTabs: true,
44336         minTabWidth: 50,
44337         preferredTabWidth: 150
44338     }
44339 });
44340
44341 // shorthand
44342 var CP = Roo.ContentPanel;
44343
44344 layout.beginUpdate();
44345 layout.add("north", new CP("north", "North"));
44346 layout.add("south", new CP("south", {title: "South", closable: true}));
44347 layout.add("west", new CP("west", {title: "West"}));
44348 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44349 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44350 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44351 layout.getRegion("center").showPanel("center1");
44352 layout.endUpdate();
44353 </code></pre>
44354
44355 <b>The container the layout is rendered into can be either the body element or any other element.
44356 If it is not the body element, the container needs to either be an absolute positioned element,
44357 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44358 the container size if it is not the body element.</b>
44359
44360 * @constructor
44361 * Create a new BorderLayout
44362 * @param {String/HTMLElement/Element} container The container this layout is bound to
44363 * @param {Object} config Configuration options
44364  */
44365 Roo.BorderLayout = function(container, config){
44366     config = config || {};
44367     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44368     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44369     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44370         var target = this.factory.validRegions[i];
44371         if(config[target]){
44372             this.addRegion(target, config[target]);
44373         }
44374     }
44375 };
44376
44377 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44378     /**
44379      * Creates and adds a new region if it doesn't already exist.
44380      * @param {String} target The target region key (north, south, east, west or center).
44381      * @param {Object} config The regions config object
44382      * @return {BorderLayoutRegion} The new region
44383      */
44384     addRegion : function(target, config){
44385         if(!this.regions[target]){
44386             var r = this.factory.create(target, this, config);
44387             this.bindRegion(target, r);
44388         }
44389         return this.regions[target];
44390     },
44391
44392     // private (kinda)
44393     bindRegion : function(name, r){
44394         this.regions[name] = r;
44395         r.on("visibilitychange", this.layout, this);
44396         r.on("paneladded", this.layout, this);
44397         r.on("panelremoved", this.layout, this);
44398         r.on("invalidated", this.layout, this);
44399         r.on("resized", this.onRegionResized, this);
44400         r.on("collapsed", this.onRegionCollapsed, this);
44401         r.on("expanded", this.onRegionExpanded, this);
44402     },
44403
44404     /**
44405      * Performs a layout update.
44406      */
44407     layout : function(){
44408         if(this.updating) return;
44409         var size = this.getViewSize();
44410         var w = size.width;
44411         var h = size.height;
44412         var centerW = w;
44413         var centerH = h;
44414         var centerY = 0;
44415         var centerX = 0;
44416         //var x = 0, y = 0;
44417
44418         var rs = this.regions;
44419         var north = rs["north"];
44420         var south = rs["south"]; 
44421         var west = rs["west"];
44422         var east = rs["east"];
44423         var center = rs["center"];
44424         //if(this.hideOnLayout){ // not supported anymore
44425             //c.el.setStyle("display", "none");
44426         //}
44427         if(north && north.isVisible()){
44428             var b = north.getBox();
44429             var m = north.getMargins();
44430             b.width = w - (m.left+m.right);
44431             b.x = m.left;
44432             b.y = m.top;
44433             centerY = b.height + b.y + m.bottom;
44434             centerH -= centerY;
44435             north.updateBox(this.safeBox(b));
44436         }
44437         if(south && south.isVisible()){
44438             var b = south.getBox();
44439             var m = south.getMargins();
44440             b.width = w - (m.left+m.right);
44441             b.x = m.left;
44442             var totalHeight = (b.height + m.top + m.bottom);
44443             b.y = h - totalHeight + m.top;
44444             centerH -= totalHeight;
44445             south.updateBox(this.safeBox(b));
44446         }
44447         if(west && west.isVisible()){
44448             var b = west.getBox();
44449             var m = west.getMargins();
44450             b.height = centerH - (m.top+m.bottom);
44451             b.x = m.left;
44452             b.y = centerY + m.top;
44453             var totalWidth = (b.width + m.left + m.right);
44454             centerX += totalWidth;
44455             centerW -= totalWidth;
44456             west.updateBox(this.safeBox(b));
44457         }
44458         if(east && east.isVisible()){
44459             var b = east.getBox();
44460             var m = east.getMargins();
44461             b.height = centerH - (m.top+m.bottom);
44462             var totalWidth = (b.width + m.left + m.right);
44463             b.x = w - totalWidth + m.left;
44464             b.y = centerY + m.top;
44465             centerW -= totalWidth;
44466             east.updateBox(this.safeBox(b));
44467         }
44468         if(center){
44469             var m = center.getMargins();
44470             var centerBox = {
44471                 x: centerX + m.left,
44472                 y: centerY + m.top,
44473                 width: centerW - (m.left+m.right),
44474                 height: centerH - (m.top+m.bottom)
44475             };
44476             //if(this.hideOnLayout){
44477                 //center.el.setStyle("display", "block");
44478             //}
44479             center.updateBox(this.safeBox(centerBox));
44480         }
44481         this.el.repaint();
44482         this.fireEvent("layout", this);
44483     },
44484
44485     // private
44486     safeBox : function(box){
44487         box.width = Math.max(0, box.width);
44488         box.height = Math.max(0, box.height);
44489         return box;
44490     },
44491
44492     /**
44493      * Adds a ContentPanel (or subclass) to this layout.
44494      * @param {String} target The target region key (north, south, east, west or center).
44495      * @param {Roo.ContentPanel} panel The panel to add
44496      * @return {Roo.ContentPanel} The added panel
44497      */
44498     add : function(target, panel){
44499          
44500         target = target.toLowerCase();
44501         return this.regions[target].add(panel);
44502     },
44503
44504     /**
44505      * Remove a ContentPanel (or subclass) to this layout.
44506      * @param {String} target The target region key (north, south, east, west or center).
44507      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44508      * @return {Roo.ContentPanel} The removed panel
44509      */
44510     remove : function(target, panel){
44511         target = target.toLowerCase();
44512         return this.regions[target].remove(panel);
44513     },
44514
44515     /**
44516      * Searches all regions for a panel with the specified id
44517      * @param {String} panelId
44518      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44519      */
44520     findPanel : function(panelId){
44521         var rs = this.regions;
44522         for(var target in rs){
44523             if(typeof rs[target] != "function"){
44524                 var p = rs[target].getPanel(panelId);
44525                 if(p){
44526                     return p;
44527                 }
44528             }
44529         }
44530         return null;
44531     },
44532
44533     /**
44534      * Searches all regions for a panel with the specified id and activates (shows) it.
44535      * @param {String/ContentPanel} panelId The panels id or the panel itself
44536      * @return {Roo.ContentPanel} The shown panel or null
44537      */
44538     showPanel : function(panelId) {
44539       var rs = this.regions;
44540       for(var target in rs){
44541          var r = rs[target];
44542          if(typeof r != "function"){
44543             if(r.hasPanel(panelId)){
44544                return r.showPanel(panelId);
44545             }
44546          }
44547       }
44548       return null;
44549    },
44550
44551    /**
44552      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44553      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44554      */
44555     restoreState : function(provider){
44556         if(!provider){
44557             provider = Roo.state.Manager;
44558         }
44559         var sm = new Roo.LayoutStateManager();
44560         sm.init(this, provider);
44561     },
44562
44563     /**
44564      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44565      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44566      * a valid ContentPanel config object.  Example:
44567      * <pre><code>
44568 // Create the main layout
44569 var layout = new Roo.BorderLayout('main-ct', {
44570     west: {
44571         split:true,
44572         minSize: 175,
44573         titlebar: true
44574     },
44575     center: {
44576         title:'Components'
44577     }
44578 }, 'main-ct');
44579
44580 // Create and add multiple ContentPanels at once via configs
44581 layout.batchAdd({
44582    west: {
44583        id: 'source-files',
44584        autoCreate:true,
44585        title:'Ext Source Files',
44586        autoScroll:true,
44587        fitToFrame:true
44588    },
44589    center : {
44590        el: cview,
44591        autoScroll:true,
44592        fitToFrame:true,
44593        toolbar: tb,
44594        resizeEl:'cbody'
44595    }
44596 });
44597 </code></pre>
44598      * @param {Object} regions An object containing ContentPanel configs by region name
44599      */
44600     batchAdd : function(regions){
44601         this.beginUpdate();
44602         for(var rname in regions){
44603             var lr = this.regions[rname];
44604             if(lr){
44605                 this.addTypedPanels(lr, regions[rname]);
44606             }
44607         }
44608         this.endUpdate();
44609     },
44610
44611     // private
44612     addTypedPanels : function(lr, ps){
44613         if(typeof ps == 'string'){
44614             lr.add(new Roo.ContentPanel(ps));
44615         }
44616         else if(ps instanceof Array){
44617             for(var i =0, len = ps.length; i < len; i++){
44618                 this.addTypedPanels(lr, ps[i]);
44619             }
44620         }
44621         else if(!ps.events){ // raw config?
44622             var el = ps.el;
44623             delete ps.el; // prevent conflict
44624             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44625         }
44626         else {  // panel object assumed!
44627             lr.add(ps);
44628         }
44629     },
44630     /**
44631      * Adds a xtype elements to the layout.
44632      * <pre><code>
44633
44634 layout.addxtype({
44635        xtype : 'ContentPanel',
44636        region: 'west',
44637        items: [ .... ]
44638    }
44639 );
44640
44641 layout.addxtype({
44642         xtype : 'NestedLayoutPanel',
44643         region: 'west',
44644         layout: {
44645            center: { },
44646            west: { }   
44647         },
44648         items : [ ... list of content panels or nested layout panels.. ]
44649    }
44650 );
44651 </code></pre>
44652      * @param {Object} cfg Xtype definition of item to add.
44653      */
44654     addxtype : function(cfg)
44655     {
44656         // basically accepts a pannel...
44657         // can accept a layout region..!?!?
44658         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44659         
44660         if (!cfg.xtype.match(/Panel$/)) {
44661             return false;
44662         }
44663         var ret = false;
44664         
44665         if (typeof(cfg.region) == 'undefined') {
44666             Roo.log("Failed to add Panel, region was not set");
44667             Roo.log(cfg);
44668             return false;
44669         }
44670         var region = cfg.region;
44671         delete cfg.region;
44672         
44673           
44674         var xitems = [];
44675         if (cfg.items) {
44676             xitems = cfg.items;
44677             delete cfg.items;
44678         }
44679         
44680         
44681         switch(cfg.xtype) 
44682         {
44683             case 'ContentPanel':  // ContentPanel (el, cfg)
44684             case 'ScrollPanel':  // ContentPanel (el, cfg)
44685                 if(cfg.autoCreate) {
44686                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44687                 } else {
44688                     var el = this.el.createChild();
44689                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44690                 }
44691                 
44692                 this.add(region, ret);
44693                 break;
44694             
44695             
44696             case 'TreePanel': // our new panel!
44697                 cfg.el = this.el.createChild();
44698                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44699                 this.add(region, ret);
44700                 break;
44701             
44702             case 'NestedLayoutPanel': 
44703                 // create a new Layout (which is  a Border Layout...
44704                 var el = this.el.createChild();
44705                 var clayout = cfg.layout;
44706                 delete cfg.layout;
44707                 clayout.items   = clayout.items  || [];
44708                 // replace this exitems with the clayout ones..
44709                 xitems = clayout.items;
44710                  
44711                 
44712                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44713                     cfg.background = false;
44714                 }
44715                 var layout = new Roo.BorderLayout(el, clayout);
44716                 
44717                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44718                 //console.log('adding nested layout panel '  + cfg.toSource());
44719                 this.add(region, ret);
44720                 
44721                 break;
44722                 
44723             case 'GridPanel': 
44724             
44725                 // needs grid and region
44726                 
44727                 //var el = this.getRegion(region).el.createChild();
44728                 var el = this.el.createChild();
44729                 // create the grid first...
44730                 
44731                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44732                 delete cfg.grid;
44733                 if (region == 'center' && this.active ) {
44734                     cfg.background = false;
44735                 }
44736                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44737                 
44738                 this.add(region, ret);
44739                 if (cfg.background) {
44740                     ret.on('activate', function(gp) {
44741                         if (!gp.grid.rendered) {
44742                             gp.grid.render();
44743                         }
44744                     });
44745                 } else {
44746                     grid.render();
44747                 }
44748                 break;
44749            
44750                
44751                 
44752                 
44753             default: 
44754                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44755                 return null;
44756              // GridPanel (grid, cfg)
44757             
44758         }
44759         this.beginUpdate();
44760         // add children..
44761         Roo.each(xitems, function(i)  {
44762             ret.addxtype(i);
44763         });
44764         this.endUpdate();
44765         return ret;
44766         
44767     }
44768 });
44769
44770 /**
44771  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44772  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44773  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44774  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44775  * <pre><code>
44776 // shorthand
44777 var CP = Roo.ContentPanel;
44778
44779 var layout = Roo.BorderLayout.create({
44780     north: {
44781         initialSize: 25,
44782         titlebar: false,
44783         panels: [new CP("north", "North")]
44784     },
44785     west: {
44786         split:true,
44787         initialSize: 200,
44788         minSize: 175,
44789         maxSize: 400,
44790         titlebar: true,
44791         collapsible: true,
44792         panels: [new CP("west", {title: "West"})]
44793     },
44794     east: {
44795         split:true,
44796         initialSize: 202,
44797         minSize: 175,
44798         maxSize: 400,
44799         titlebar: true,
44800         collapsible: true,
44801         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44802     },
44803     south: {
44804         split:true,
44805         initialSize: 100,
44806         minSize: 100,
44807         maxSize: 200,
44808         titlebar: true,
44809         collapsible: true,
44810         panels: [new CP("south", {title: "South", closable: true})]
44811     },
44812     center: {
44813         titlebar: true,
44814         autoScroll:true,
44815         resizeTabs: true,
44816         minTabWidth: 50,
44817         preferredTabWidth: 150,
44818         panels: [
44819             new CP("center1", {title: "Close Me", closable: true}),
44820             new CP("center2", {title: "Center Panel", closable: false})
44821         ]
44822     }
44823 }, document.body);
44824
44825 layout.getRegion("center").showPanel("center1");
44826 </code></pre>
44827  * @param config
44828  * @param targetEl
44829  */
44830 Roo.BorderLayout.create = function(config, targetEl){
44831     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44832     layout.beginUpdate();
44833     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44834     for(var j = 0, jlen = regions.length; j < jlen; j++){
44835         var lr = regions[j];
44836         if(layout.regions[lr] && config[lr].panels){
44837             var r = layout.regions[lr];
44838             var ps = config[lr].panels;
44839             layout.addTypedPanels(r, ps);
44840         }
44841     }
44842     layout.endUpdate();
44843     return layout;
44844 };
44845
44846 // private
44847 Roo.BorderLayout.RegionFactory = {
44848     // private
44849     validRegions : ["north","south","east","west","center"],
44850
44851     // private
44852     create : function(target, mgr, config){
44853         target = target.toLowerCase();
44854         if(config.lightweight || config.basic){
44855             return new Roo.BasicLayoutRegion(mgr, config, target);
44856         }
44857         switch(target){
44858             case "north":
44859                 return new Roo.NorthLayoutRegion(mgr, config);
44860             case "south":
44861                 return new Roo.SouthLayoutRegion(mgr, config);
44862             case "east":
44863                 return new Roo.EastLayoutRegion(mgr, config);
44864             case "west":
44865                 return new Roo.WestLayoutRegion(mgr, config);
44866             case "center":
44867                 return new Roo.CenterLayoutRegion(mgr, config);
44868         }
44869         throw 'Layout region "'+target+'" not supported.';
44870     }
44871 };/*
44872  * Based on:
44873  * Ext JS Library 1.1.1
44874  * Copyright(c) 2006-2007, Ext JS, LLC.
44875  *
44876  * Originally Released Under LGPL - original licence link has changed is not relivant.
44877  *
44878  * Fork - LGPL
44879  * <script type="text/javascript">
44880  */
44881  
44882 /**
44883  * @class Roo.BasicLayoutRegion
44884  * @extends Roo.util.Observable
44885  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44886  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44887  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44888  */
44889 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44890     this.mgr = mgr;
44891     this.position  = pos;
44892     this.events = {
44893         /**
44894          * @scope Roo.BasicLayoutRegion
44895          */
44896         
44897         /**
44898          * @event beforeremove
44899          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44900          * @param {Roo.LayoutRegion} this
44901          * @param {Roo.ContentPanel} panel The panel
44902          * @param {Object} e The cancel event object
44903          */
44904         "beforeremove" : true,
44905         /**
44906          * @event invalidated
44907          * Fires when the layout for this region is changed.
44908          * @param {Roo.LayoutRegion} this
44909          */
44910         "invalidated" : true,
44911         /**
44912          * @event visibilitychange
44913          * Fires when this region is shown or hidden 
44914          * @param {Roo.LayoutRegion} this
44915          * @param {Boolean} visibility true or false
44916          */
44917         "visibilitychange" : true,
44918         /**
44919          * @event paneladded
44920          * Fires when a panel is added. 
44921          * @param {Roo.LayoutRegion} this
44922          * @param {Roo.ContentPanel} panel The panel
44923          */
44924         "paneladded" : true,
44925         /**
44926          * @event panelremoved
44927          * Fires when a panel is removed. 
44928          * @param {Roo.LayoutRegion} this
44929          * @param {Roo.ContentPanel} panel The panel
44930          */
44931         "panelremoved" : true,
44932         /**
44933          * @event collapsed
44934          * Fires when this region is collapsed.
44935          * @param {Roo.LayoutRegion} this
44936          */
44937         "collapsed" : true,
44938         /**
44939          * @event expanded
44940          * Fires when this region is expanded.
44941          * @param {Roo.LayoutRegion} this
44942          */
44943         "expanded" : true,
44944         /**
44945          * @event slideshow
44946          * Fires when this region is slid into view.
44947          * @param {Roo.LayoutRegion} this
44948          */
44949         "slideshow" : true,
44950         /**
44951          * @event slidehide
44952          * Fires when this region slides out of view. 
44953          * @param {Roo.LayoutRegion} this
44954          */
44955         "slidehide" : true,
44956         /**
44957          * @event panelactivated
44958          * Fires when a panel is activated. 
44959          * @param {Roo.LayoutRegion} this
44960          * @param {Roo.ContentPanel} panel The activated panel
44961          */
44962         "panelactivated" : true,
44963         /**
44964          * @event resized
44965          * Fires when the user resizes this region. 
44966          * @param {Roo.LayoutRegion} this
44967          * @param {Number} newSize The new size (width for east/west, height for north/south)
44968          */
44969         "resized" : true
44970     };
44971     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44972     this.panels = new Roo.util.MixedCollection();
44973     this.panels.getKey = this.getPanelId.createDelegate(this);
44974     this.box = null;
44975     this.activePanel = null;
44976     // ensure listeners are added...
44977     
44978     if (config.listeners || config.events) {
44979         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44980             listeners : config.listeners || {},
44981             events : config.events || {}
44982         });
44983     }
44984     
44985     if(skipConfig !== true){
44986         this.applyConfig(config);
44987     }
44988 };
44989
44990 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44991     getPanelId : function(p){
44992         return p.getId();
44993     },
44994     
44995     applyConfig : function(config){
44996         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44997         this.config = config;
44998         
44999     },
45000     
45001     /**
45002      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45003      * the width, for horizontal (north, south) the height.
45004      * @param {Number} newSize The new width or height
45005      */
45006     resizeTo : function(newSize){
45007         var el = this.el ? this.el :
45008                  (this.activePanel ? this.activePanel.getEl() : null);
45009         if(el){
45010             switch(this.position){
45011                 case "east":
45012                 case "west":
45013                     el.setWidth(newSize);
45014                     this.fireEvent("resized", this, newSize);
45015                 break;
45016                 case "north":
45017                 case "south":
45018                     el.setHeight(newSize);
45019                     this.fireEvent("resized", this, newSize);
45020                 break;                
45021             }
45022         }
45023     },
45024     
45025     getBox : function(){
45026         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45027     },
45028     
45029     getMargins : function(){
45030         return this.margins;
45031     },
45032     
45033     updateBox : function(box){
45034         this.box = box;
45035         var el = this.activePanel.getEl();
45036         el.dom.style.left = box.x + "px";
45037         el.dom.style.top = box.y + "px";
45038         this.activePanel.setSize(box.width, box.height);
45039     },
45040     
45041     /**
45042      * Returns the container element for this region.
45043      * @return {Roo.Element}
45044      */
45045     getEl : function(){
45046         return this.activePanel;
45047     },
45048     
45049     /**
45050      * Returns true if this region is currently visible.
45051      * @return {Boolean}
45052      */
45053     isVisible : function(){
45054         return this.activePanel ? true : false;
45055     },
45056     
45057     setActivePanel : function(panel){
45058         panel = this.getPanel(panel);
45059         if(this.activePanel && this.activePanel != panel){
45060             this.activePanel.setActiveState(false);
45061             this.activePanel.getEl().setLeftTop(-10000,-10000);
45062         }
45063         this.activePanel = panel;
45064         panel.setActiveState(true);
45065         if(this.box){
45066             panel.setSize(this.box.width, this.box.height);
45067         }
45068         this.fireEvent("panelactivated", this, panel);
45069         this.fireEvent("invalidated");
45070     },
45071     
45072     /**
45073      * Show the specified panel.
45074      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45075      * @return {Roo.ContentPanel} The shown panel or null
45076      */
45077     showPanel : function(panel){
45078         if(panel = this.getPanel(panel)){
45079             this.setActivePanel(panel);
45080         }
45081         return panel;
45082     },
45083     
45084     /**
45085      * Get the active panel for this region.
45086      * @return {Roo.ContentPanel} The active panel or null
45087      */
45088     getActivePanel : function(){
45089         return this.activePanel;
45090     },
45091     
45092     /**
45093      * Add the passed ContentPanel(s)
45094      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45095      * @return {Roo.ContentPanel} The panel added (if only one was added)
45096      */
45097     add : function(panel){
45098         if(arguments.length > 1){
45099             for(var i = 0, len = arguments.length; i < len; i++) {
45100                 this.add(arguments[i]);
45101             }
45102             return null;
45103         }
45104         if(this.hasPanel(panel)){
45105             this.showPanel(panel);
45106             return panel;
45107         }
45108         var el = panel.getEl();
45109         if(el.dom.parentNode != this.mgr.el.dom){
45110             this.mgr.el.dom.appendChild(el.dom);
45111         }
45112         if(panel.setRegion){
45113             panel.setRegion(this);
45114         }
45115         this.panels.add(panel);
45116         el.setStyle("position", "absolute");
45117         if(!panel.background){
45118             this.setActivePanel(panel);
45119             if(this.config.initialSize && this.panels.getCount()==1){
45120                 this.resizeTo(this.config.initialSize);
45121             }
45122         }
45123         this.fireEvent("paneladded", this, panel);
45124         return panel;
45125     },
45126     
45127     /**
45128      * Returns true if the panel is in this region.
45129      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45130      * @return {Boolean}
45131      */
45132     hasPanel : function(panel){
45133         if(typeof panel == "object"){ // must be panel obj
45134             panel = panel.getId();
45135         }
45136         return this.getPanel(panel) ? true : false;
45137     },
45138     
45139     /**
45140      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45141      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45142      * @param {Boolean} preservePanel Overrides the config preservePanel option
45143      * @return {Roo.ContentPanel} The panel that was removed
45144      */
45145     remove : function(panel, preservePanel){
45146         panel = this.getPanel(panel);
45147         if(!panel){
45148             return null;
45149         }
45150         var e = {};
45151         this.fireEvent("beforeremove", this, panel, e);
45152         if(e.cancel === true){
45153             return null;
45154         }
45155         var panelId = panel.getId();
45156         this.panels.removeKey(panelId);
45157         return panel;
45158     },
45159     
45160     /**
45161      * Returns the panel specified or null if it's not in this region.
45162      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45163      * @return {Roo.ContentPanel}
45164      */
45165     getPanel : function(id){
45166         if(typeof id == "object"){ // must be panel obj
45167             return id;
45168         }
45169         return this.panels.get(id);
45170     },
45171     
45172     /**
45173      * Returns this regions position (north/south/east/west/center).
45174      * @return {String} 
45175      */
45176     getPosition: function(){
45177         return this.position;    
45178     }
45179 });/*
45180  * Based on:
45181  * Ext JS Library 1.1.1
45182  * Copyright(c) 2006-2007, Ext JS, LLC.
45183  *
45184  * Originally Released Under LGPL - original licence link has changed is not relivant.
45185  *
45186  * Fork - LGPL
45187  * <script type="text/javascript">
45188  */
45189  
45190 /**
45191  * @class Roo.LayoutRegion
45192  * @extends Roo.BasicLayoutRegion
45193  * This class represents a region in a layout manager.
45194  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45195  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45196  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45197  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45198  * @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})
45199  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45200  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45201  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45202  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45203  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45204  * @cfg {String}    title           The title for the region (overrides panel titles)
45205  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45206  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45207  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45208  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45209  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45210  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45211  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45212  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45213  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45214  * @cfg {Boolean}   showPin         True to show a pin button
45215  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45216  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45217  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45218  * @cfg {Number}    width           For East/West panels
45219  * @cfg {Number}    height          For North/South panels
45220  * @cfg {Boolean}   split           To show the splitter
45221  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45222  */
45223 Roo.LayoutRegion = function(mgr, config, pos){
45224     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45225     var dh = Roo.DomHelper;
45226     /** This region's container element 
45227     * @type Roo.Element */
45228     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45229     /** This region's title element 
45230     * @type Roo.Element */
45231
45232     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45233         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45234         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45235     ]}, true);
45236     this.titleEl.enableDisplayMode();
45237     /** This region's title text element 
45238     * @type HTMLElement */
45239     this.titleTextEl = this.titleEl.dom.firstChild;
45240     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45241     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45242     this.closeBtn.enableDisplayMode();
45243     this.closeBtn.on("click", this.closeClicked, this);
45244     this.closeBtn.hide();
45245
45246     this.createBody(config);
45247     this.visible = true;
45248     this.collapsed = false;
45249
45250     if(config.hideWhenEmpty){
45251         this.hide();
45252         this.on("paneladded", this.validateVisibility, this);
45253         this.on("panelremoved", this.validateVisibility, this);
45254     }
45255     this.applyConfig(config);
45256 };
45257
45258 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45259
45260     createBody : function(){
45261         /** This region's body element 
45262         * @type Roo.Element */
45263         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45264     },
45265
45266     applyConfig : function(c){
45267         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45268             var dh = Roo.DomHelper;
45269             if(c.titlebar !== false){
45270                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45271                 this.collapseBtn.on("click", this.collapse, this);
45272                 this.collapseBtn.enableDisplayMode();
45273
45274                 if(c.showPin === true || this.showPin){
45275                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45276                     this.stickBtn.enableDisplayMode();
45277                     this.stickBtn.on("click", this.expand, this);
45278                     this.stickBtn.hide();
45279                 }
45280             }
45281             /** This region's collapsed element
45282             * @type Roo.Element */
45283             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45284                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45285             ]}, true);
45286             if(c.floatable !== false){
45287                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45288                this.collapsedEl.on("click", this.collapseClick, this);
45289             }
45290
45291             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45292                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45293                    id: "message", unselectable: "on", style:{"float":"left"}});
45294                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45295              }
45296             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45297             this.expandBtn.on("click", this.expand, this);
45298         }
45299         if(this.collapseBtn){
45300             this.collapseBtn.setVisible(c.collapsible == true);
45301         }
45302         this.cmargins = c.cmargins || this.cmargins ||
45303                          (this.position == "west" || this.position == "east" ?
45304                              {top: 0, left: 2, right:2, bottom: 0} :
45305                              {top: 2, left: 0, right:0, bottom: 2});
45306         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45307         this.bottomTabs = c.tabPosition != "top";
45308         this.autoScroll = c.autoScroll || false;
45309         if(this.autoScroll){
45310             this.bodyEl.setStyle("overflow", "auto");
45311         }else{
45312             this.bodyEl.setStyle("overflow", "hidden");
45313         }
45314         //if(c.titlebar !== false){
45315             if((!c.titlebar && !c.title) || c.titlebar === false){
45316                 this.titleEl.hide();
45317             }else{
45318                 this.titleEl.show();
45319                 if(c.title){
45320                     this.titleTextEl.innerHTML = c.title;
45321                 }
45322             }
45323         //}
45324         this.duration = c.duration || .30;
45325         this.slideDuration = c.slideDuration || .45;
45326         this.config = c;
45327         if(c.collapsed){
45328             this.collapse(true);
45329         }
45330         if(c.hidden){
45331             this.hide();
45332         }
45333     },
45334     /**
45335      * Returns true if this region is currently visible.
45336      * @return {Boolean}
45337      */
45338     isVisible : function(){
45339         return this.visible;
45340     },
45341
45342     /**
45343      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45344      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45345      */
45346     setCollapsedTitle : function(title){
45347         title = title || "&#160;";
45348         if(this.collapsedTitleTextEl){
45349             this.collapsedTitleTextEl.innerHTML = title;
45350         }
45351     },
45352
45353     getBox : function(){
45354         var b;
45355         if(!this.collapsed){
45356             b = this.el.getBox(false, true);
45357         }else{
45358             b = this.collapsedEl.getBox(false, true);
45359         }
45360         return b;
45361     },
45362
45363     getMargins : function(){
45364         return this.collapsed ? this.cmargins : this.margins;
45365     },
45366
45367     highlight : function(){
45368         this.el.addClass("x-layout-panel-dragover");
45369     },
45370
45371     unhighlight : function(){
45372         this.el.removeClass("x-layout-panel-dragover");
45373     },
45374
45375     updateBox : function(box){
45376         this.box = box;
45377         if(!this.collapsed){
45378             this.el.dom.style.left = box.x + "px";
45379             this.el.dom.style.top = box.y + "px";
45380             this.updateBody(box.width, box.height);
45381         }else{
45382             this.collapsedEl.dom.style.left = box.x + "px";
45383             this.collapsedEl.dom.style.top = box.y + "px";
45384             this.collapsedEl.setSize(box.width, box.height);
45385         }
45386         if(this.tabs){
45387             this.tabs.autoSizeTabs();
45388         }
45389     },
45390
45391     updateBody : function(w, h){
45392         if(w !== null){
45393             this.el.setWidth(w);
45394             w -= this.el.getBorderWidth("rl");
45395             if(this.config.adjustments){
45396                 w += this.config.adjustments[0];
45397             }
45398         }
45399         if(h !== null){
45400             this.el.setHeight(h);
45401             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45402             h -= this.el.getBorderWidth("tb");
45403             if(this.config.adjustments){
45404                 h += this.config.adjustments[1];
45405             }
45406             this.bodyEl.setHeight(h);
45407             if(this.tabs){
45408                 h = this.tabs.syncHeight(h);
45409             }
45410         }
45411         if(this.panelSize){
45412             w = w !== null ? w : this.panelSize.width;
45413             h = h !== null ? h : this.panelSize.height;
45414         }
45415         if(this.activePanel){
45416             var el = this.activePanel.getEl();
45417             w = w !== null ? w : el.getWidth();
45418             h = h !== null ? h : el.getHeight();
45419             this.panelSize = {width: w, height: h};
45420             this.activePanel.setSize(w, h);
45421         }
45422         if(Roo.isIE && this.tabs){
45423             this.tabs.el.repaint();
45424         }
45425     },
45426
45427     /**
45428      * Returns the container element for this region.
45429      * @return {Roo.Element}
45430      */
45431     getEl : function(){
45432         return this.el;
45433     },
45434
45435     /**
45436      * Hides this region.
45437      */
45438     hide : function(){
45439         if(!this.collapsed){
45440             this.el.dom.style.left = "-2000px";
45441             this.el.hide();
45442         }else{
45443             this.collapsedEl.dom.style.left = "-2000px";
45444             this.collapsedEl.hide();
45445         }
45446         this.visible = false;
45447         this.fireEvent("visibilitychange", this, false);
45448     },
45449
45450     /**
45451      * Shows this region if it was previously hidden.
45452      */
45453     show : function(){
45454         if(!this.collapsed){
45455             this.el.show();
45456         }else{
45457             this.collapsedEl.show();
45458         }
45459         this.visible = true;
45460         this.fireEvent("visibilitychange", this, true);
45461     },
45462
45463     closeClicked : function(){
45464         if(this.activePanel){
45465             this.remove(this.activePanel);
45466         }
45467     },
45468
45469     collapseClick : function(e){
45470         if(this.isSlid){
45471            e.stopPropagation();
45472            this.slideIn();
45473         }else{
45474            e.stopPropagation();
45475            this.slideOut();
45476         }
45477     },
45478
45479     /**
45480      * Collapses this region.
45481      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45482      */
45483     collapse : function(skipAnim){
45484         if(this.collapsed) return;
45485         this.collapsed = true;
45486         if(this.split){
45487             this.split.el.hide();
45488         }
45489         if(this.config.animate && skipAnim !== true){
45490             this.fireEvent("invalidated", this);
45491             this.animateCollapse();
45492         }else{
45493             this.el.setLocation(-20000,-20000);
45494             this.el.hide();
45495             this.collapsedEl.show();
45496             this.fireEvent("collapsed", this);
45497             this.fireEvent("invalidated", this);
45498         }
45499     },
45500
45501     animateCollapse : function(){
45502         // overridden
45503     },
45504
45505     /**
45506      * Expands this region if it was previously collapsed.
45507      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45508      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45509      */
45510     expand : function(e, skipAnim){
45511         if(e) e.stopPropagation();
45512         if(!this.collapsed || this.el.hasActiveFx()) return;
45513         if(this.isSlid){
45514             this.afterSlideIn();
45515             skipAnim = true;
45516         }
45517         this.collapsed = false;
45518         if(this.config.animate && skipAnim !== true){
45519             this.animateExpand();
45520         }else{
45521             this.el.show();
45522             if(this.split){
45523                 this.split.el.show();
45524             }
45525             this.collapsedEl.setLocation(-2000,-2000);
45526             this.collapsedEl.hide();
45527             this.fireEvent("invalidated", this);
45528             this.fireEvent("expanded", this);
45529         }
45530     },
45531
45532     animateExpand : function(){
45533         // overridden
45534     },
45535
45536     initTabs : function()
45537     {
45538         this.bodyEl.setStyle("overflow", "hidden");
45539         var ts = new Roo.TabPanel(
45540                 this.bodyEl.dom,
45541                 {
45542                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45543                     disableTooltips: this.config.disableTabTips,
45544                     toolbar : this.config.toolbar
45545                 }
45546         );
45547         if(this.config.hideTabs){
45548             ts.stripWrap.setDisplayed(false);
45549         }
45550         this.tabs = ts;
45551         ts.resizeTabs = this.config.resizeTabs === true;
45552         ts.minTabWidth = this.config.minTabWidth || 40;
45553         ts.maxTabWidth = this.config.maxTabWidth || 250;
45554         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45555         ts.monitorResize = false;
45556         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45557         ts.bodyEl.addClass('x-layout-tabs-body');
45558         this.panels.each(this.initPanelAsTab, this);
45559     },
45560
45561     initPanelAsTab : function(panel){
45562         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45563                     this.config.closeOnTab && panel.isClosable());
45564         if(panel.tabTip !== undefined){
45565             ti.setTooltip(panel.tabTip);
45566         }
45567         ti.on("activate", function(){
45568               this.setActivePanel(panel);
45569         }, this);
45570         if(this.config.closeOnTab){
45571             ti.on("beforeclose", function(t, e){
45572                 e.cancel = true;
45573                 this.remove(panel);
45574             }, this);
45575         }
45576         return ti;
45577     },
45578
45579     updatePanelTitle : function(panel, title){
45580         if(this.activePanel == panel){
45581             this.updateTitle(title);
45582         }
45583         if(this.tabs){
45584             var ti = this.tabs.getTab(panel.getEl().id);
45585             ti.setText(title);
45586             if(panel.tabTip !== undefined){
45587                 ti.setTooltip(panel.tabTip);
45588             }
45589         }
45590     },
45591
45592     updateTitle : function(title){
45593         if(this.titleTextEl && !this.config.title){
45594             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45595         }
45596     },
45597
45598     setActivePanel : function(panel){
45599         panel = this.getPanel(panel);
45600         if(this.activePanel && this.activePanel != panel){
45601             this.activePanel.setActiveState(false);
45602         }
45603         this.activePanel = panel;
45604         panel.setActiveState(true);
45605         if(this.panelSize){
45606             panel.setSize(this.panelSize.width, this.panelSize.height);
45607         }
45608         if(this.closeBtn){
45609             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45610         }
45611         this.updateTitle(panel.getTitle());
45612         if(this.tabs){
45613             this.fireEvent("invalidated", this);
45614         }
45615         this.fireEvent("panelactivated", this, panel);
45616     },
45617
45618     /**
45619      * Shows the specified panel.
45620      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45621      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45622      */
45623     showPanel : function(panel){
45624         if(panel = this.getPanel(panel)){
45625             if(this.tabs){
45626                 var tab = this.tabs.getTab(panel.getEl().id);
45627                 if(tab.isHidden()){
45628                     this.tabs.unhideTab(tab.id);
45629                 }
45630                 tab.activate();
45631             }else{
45632                 this.setActivePanel(panel);
45633             }
45634         }
45635         return panel;
45636     },
45637
45638     /**
45639      * Get the active panel for this region.
45640      * @return {Roo.ContentPanel} The active panel or null
45641      */
45642     getActivePanel : function(){
45643         return this.activePanel;
45644     },
45645
45646     validateVisibility : function(){
45647         if(this.panels.getCount() < 1){
45648             this.updateTitle("&#160;");
45649             this.closeBtn.hide();
45650             this.hide();
45651         }else{
45652             if(!this.isVisible()){
45653                 this.show();
45654             }
45655         }
45656     },
45657
45658     /**
45659      * Adds the passed ContentPanel(s) to this region.
45660      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45661      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45662      */
45663     add : function(panel){
45664         if(arguments.length > 1){
45665             for(var i = 0, len = arguments.length; i < len; i++) {
45666                 this.add(arguments[i]);
45667             }
45668             return null;
45669         }
45670         if(this.hasPanel(panel)){
45671             this.showPanel(panel);
45672             return panel;
45673         }
45674         panel.setRegion(this);
45675         this.panels.add(panel);
45676         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45677             this.bodyEl.dom.appendChild(panel.getEl().dom);
45678             if(panel.background !== true){
45679                 this.setActivePanel(panel);
45680             }
45681             this.fireEvent("paneladded", this, panel);
45682             return panel;
45683         }
45684         if(!this.tabs){
45685             this.initTabs();
45686         }else{
45687             this.initPanelAsTab(panel);
45688         }
45689         if(panel.background !== true){
45690             this.tabs.activate(panel.getEl().id);
45691         }
45692         this.fireEvent("paneladded", this, panel);
45693         return panel;
45694     },
45695
45696     /**
45697      * Hides the tab for the specified panel.
45698      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45699      */
45700     hidePanel : function(panel){
45701         if(this.tabs && (panel = this.getPanel(panel))){
45702             this.tabs.hideTab(panel.getEl().id);
45703         }
45704     },
45705
45706     /**
45707      * Unhides the tab for a previously hidden panel.
45708      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45709      */
45710     unhidePanel : function(panel){
45711         if(this.tabs && (panel = this.getPanel(panel))){
45712             this.tabs.unhideTab(panel.getEl().id);
45713         }
45714     },
45715
45716     clearPanels : function(){
45717         while(this.panels.getCount() > 0){
45718              this.remove(this.panels.first());
45719         }
45720     },
45721
45722     /**
45723      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45724      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45725      * @param {Boolean} preservePanel Overrides the config preservePanel option
45726      * @return {Roo.ContentPanel} The panel that was removed
45727      */
45728     remove : function(panel, preservePanel){
45729         panel = this.getPanel(panel);
45730         if(!panel){
45731             return null;
45732         }
45733         var e = {};
45734         this.fireEvent("beforeremove", this, panel, e);
45735         if(e.cancel === true){
45736             return null;
45737         }
45738         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45739         var panelId = panel.getId();
45740         this.panels.removeKey(panelId);
45741         if(preservePanel){
45742             document.body.appendChild(panel.getEl().dom);
45743         }
45744         if(this.tabs){
45745             this.tabs.removeTab(panel.getEl().id);
45746         }else if (!preservePanel){
45747             this.bodyEl.dom.removeChild(panel.getEl().dom);
45748         }
45749         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45750             var p = this.panels.first();
45751             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45752             tempEl.appendChild(p.getEl().dom);
45753             this.bodyEl.update("");
45754             this.bodyEl.dom.appendChild(p.getEl().dom);
45755             tempEl = null;
45756             this.updateTitle(p.getTitle());
45757             this.tabs = null;
45758             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45759             this.setActivePanel(p);
45760         }
45761         panel.setRegion(null);
45762         if(this.activePanel == panel){
45763             this.activePanel = null;
45764         }
45765         if(this.config.autoDestroy !== false && preservePanel !== true){
45766             try{panel.destroy();}catch(e){}
45767         }
45768         this.fireEvent("panelremoved", this, panel);
45769         return panel;
45770     },
45771
45772     /**
45773      * Returns the TabPanel component used by this region
45774      * @return {Roo.TabPanel}
45775      */
45776     getTabs : function(){
45777         return this.tabs;
45778     },
45779
45780     createTool : function(parentEl, className){
45781         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45782             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45783         btn.addClassOnOver("x-layout-tools-button-over");
45784         return btn;
45785     }
45786 });/*
45787  * Based on:
45788  * Ext JS Library 1.1.1
45789  * Copyright(c) 2006-2007, Ext JS, LLC.
45790  *
45791  * Originally Released Under LGPL - original licence link has changed is not relivant.
45792  *
45793  * Fork - LGPL
45794  * <script type="text/javascript">
45795  */
45796  
45797
45798
45799 /**
45800  * @class Roo.SplitLayoutRegion
45801  * @extends Roo.LayoutRegion
45802  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45803  */
45804 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45805     this.cursor = cursor;
45806     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45807 };
45808
45809 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45810     splitTip : "Drag to resize.",
45811     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45812     useSplitTips : false,
45813
45814     applyConfig : function(config){
45815         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45816         if(config.split){
45817             if(!this.split){
45818                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45819                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45820                 /** The SplitBar for this region 
45821                 * @type Roo.SplitBar */
45822                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45823                 this.split.on("moved", this.onSplitMove, this);
45824                 this.split.useShim = config.useShim === true;
45825                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45826                 if(this.useSplitTips){
45827                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45828                 }
45829                 if(config.collapsible){
45830                     this.split.el.on("dblclick", this.collapse,  this);
45831                 }
45832             }
45833             if(typeof config.minSize != "undefined"){
45834                 this.split.minSize = config.minSize;
45835             }
45836             if(typeof config.maxSize != "undefined"){
45837                 this.split.maxSize = config.maxSize;
45838             }
45839             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45840                 this.hideSplitter();
45841             }
45842         }
45843     },
45844
45845     getHMaxSize : function(){
45846          var cmax = this.config.maxSize || 10000;
45847          var center = this.mgr.getRegion("center");
45848          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45849     },
45850
45851     getVMaxSize : function(){
45852          var cmax = this.config.maxSize || 10000;
45853          var center = this.mgr.getRegion("center");
45854          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45855     },
45856
45857     onSplitMove : function(split, newSize){
45858         this.fireEvent("resized", this, newSize);
45859     },
45860     
45861     /** 
45862      * Returns the {@link Roo.SplitBar} for this region.
45863      * @return {Roo.SplitBar}
45864      */
45865     getSplitBar : function(){
45866         return this.split;
45867     },
45868     
45869     hide : function(){
45870         this.hideSplitter();
45871         Roo.SplitLayoutRegion.superclass.hide.call(this);
45872     },
45873
45874     hideSplitter : function(){
45875         if(this.split){
45876             this.split.el.setLocation(-2000,-2000);
45877             this.split.el.hide();
45878         }
45879     },
45880
45881     show : function(){
45882         if(this.split){
45883             this.split.el.show();
45884         }
45885         Roo.SplitLayoutRegion.superclass.show.call(this);
45886     },
45887     
45888     beforeSlide: function(){
45889         if(Roo.isGecko){// firefox overflow auto bug workaround
45890             this.bodyEl.clip();
45891             if(this.tabs) this.tabs.bodyEl.clip();
45892             if(this.activePanel){
45893                 this.activePanel.getEl().clip();
45894                 
45895                 if(this.activePanel.beforeSlide){
45896                     this.activePanel.beforeSlide();
45897                 }
45898             }
45899         }
45900     },
45901     
45902     afterSlide : function(){
45903         if(Roo.isGecko){// firefox overflow auto bug workaround
45904             this.bodyEl.unclip();
45905             if(this.tabs) this.tabs.bodyEl.unclip();
45906             if(this.activePanel){
45907                 this.activePanel.getEl().unclip();
45908                 if(this.activePanel.afterSlide){
45909                     this.activePanel.afterSlide();
45910                 }
45911             }
45912         }
45913     },
45914
45915     initAutoHide : function(){
45916         if(this.autoHide !== false){
45917             if(!this.autoHideHd){
45918                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45919                 this.autoHideHd = {
45920                     "mouseout": function(e){
45921                         if(!e.within(this.el, true)){
45922                             st.delay(500);
45923                         }
45924                     },
45925                     "mouseover" : function(e){
45926                         st.cancel();
45927                     },
45928                     scope : this
45929                 };
45930             }
45931             this.el.on(this.autoHideHd);
45932         }
45933     },
45934
45935     clearAutoHide : function(){
45936         if(this.autoHide !== false){
45937             this.el.un("mouseout", this.autoHideHd.mouseout);
45938             this.el.un("mouseover", this.autoHideHd.mouseover);
45939         }
45940     },
45941
45942     clearMonitor : function(){
45943         Roo.get(document).un("click", this.slideInIf, this);
45944     },
45945
45946     // these names are backwards but not changed for compat
45947     slideOut : function(){
45948         if(this.isSlid || this.el.hasActiveFx()){
45949             return;
45950         }
45951         this.isSlid = true;
45952         if(this.collapseBtn){
45953             this.collapseBtn.hide();
45954         }
45955         this.closeBtnState = this.closeBtn.getStyle('display');
45956         this.closeBtn.hide();
45957         if(this.stickBtn){
45958             this.stickBtn.show();
45959         }
45960         this.el.show();
45961         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45962         this.beforeSlide();
45963         this.el.setStyle("z-index", 10001);
45964         this.el.slideIn(this.getSlideAnchor(), {
45965             callback: function(){
45966                 this.afterSlide();
45967                 this.initAutoHide();
45968                 Roo.get(document).on("click", this.slideInIf, this);
45969                 this.fireEvent("slideshow", this);
45970             },
45971             scope: this,
45972             block: true
45973         });
45974     },
45975
45976     afterSlideIn : function(){
45977         this.clearAutoHide();
45978         this.isSlid = false;
45979         this.clearMonitor();
45980         this.el.setStyle("z-index", "");
45981         if(this.collapseBtn){
45982             this.collapseBtn.show();
45983         }
45984         this.closeBtn.setStyle('display', this.closeBtnState);
45985         if(this.stickBtn){
45986             this.stickBtn.hide();
45987         }
45988         this.fireEvent("slidehide", this);
45989     },
45990
45991     slideIn : function(cb){
45992         if(!this.isSlid || this.el.hasActiveFx()){
45993             Roo.callback(cb);
45994             return;
45995         }
45996         this.isSlid = false;
45997         this.beforeSlide();
45998         this.el.slideOut(this.getSlideAnchor(), {
45999             callback: function(){
46000                 this.el.setLeftTop(-10000, -10000);
46001                 this.afterSlide();
46002                 this.afterSlideIn();
46003                 Roo.callback(cb);
46004             },
46005             scope: this,
46006             block: true
46007         });
46008     },
46009     
46010     slideInIf : function(e){
46011         if(!e.within(this.el)){
46012             this.slideIn();
46013         }
46014     },
46015
46016     animateCollapse : function(){
46017         this.beforeSlide();
46018         this.el.setStyle("z-index", 20000);
46019         var anchor = this.getSlideAnchor();
46020         this.el.slideOut(anchor, {
46021             callback : function(){
46022                 this.el.setStyle("z-index", "");
46023                 this.collapsedEl.slideIn(anchor, {duration:.3});
46024                 this.afterSlide();
46025                 this.el.setLocation(-10000,-10000);
46026                 this.el.hide();
46027                 this.fireEvent("collapsed", this);
46028             },
46029             scope: this,
46030             block: true
46031         });
46032     },
46033
46034     animateExpand : function(){
46035         this.beforeSlide();
46036         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46037         this.el.setStyle("z-index", 20000);
46038         this.collapsedEl.hide({
46039             duration:.1
46040         });
46041         this.el.slideIn(this.getSlideAnchor(), {
46042             callback : function(){
46043                 this.el.setStyle("z-index", "");
46044                 this.afterSlide();
46045                 if(this.split){
46046                     this.split.el.show();
46047                 }
46048                 this.fireEvent("invalidated", this);
46049                 this.fireEvent("expanded", this);
46050             },
46051             scope: this,
46052             block: true
46053         });
46054     },
46055
46056     anchors : {
46057         "west" : "left",
46058         "east" : "right",
46059         "north" : "top",
46060         "south" : "bottom"
46061     },
46062
46063     sanchors : {
46064         "west" : "l",
46065         "east" : "r",
46066         "north" : "t",
46067         "south" : "b"
46068     },
46069
46070     canchors : {
46071         "west" : "tl-tr",
46072         "east" : "tr-tl",
46073         "north" : "tl-bl",
46074         "south" : "bl-tl"
46075     },
46076
46077     getAnchor : function(){
46078         return this.anchors[this.position];
46079     },
46080
46081     getCollapseAnchor : function(){
46082         return this.canchors[this.position];
46083     },
46084
46085     getSlideAnchor : function(){
46086         return this.sanchors[this.position];
46087     },
46088
46089     getAlignAdj : function(){
46090         var cm = this.cmargins;
46091         switch(this.position){
46092             case "west":
46093                 return [0, 0];
46094             break;
46095             case "east":
46096                 return [0, 0];
46097             break;
46098             case "north":
46099                 return [0, 0];
46100             break;
46101             case "south":
46102                 return [0, 0];
46103             break;
46104         }
46105     },
46106
46107     getExpandAdj : function(){
46108         var c = this.collapsedEl, cm = this.cmargins;
46109         switch(this.position){
46110             case "west":
46111                 return [-(cm.right+c.getWidth()+cm.left), 0];
46112             break;
46113             case "east":
46114                 return [cm.right+c.getWidth()+cm.left, 0];
46115             break;
46116             case "north":
46117                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46118             break;
46119             case "south":
46120                 return [0, cm.top+cm.bottom+c.getHeight()];
46121             break;
46122         }
46123     }
46124 });/*
46125  * Based on:
46126  * Ext JS Library 1.1.1
46127  * Copyright(c) 2006-2007, Ext JS, LLC.
46128  *
46129  * Originally Released Under LGPL - original licence link has changed is not relivant.
46130  *
46131  * Fork - LGPL
46132  * <script type="text/javascript">
46133  */
46134 /*
46135  * These classes are private internal classes
46136  */
46137 Roo.CenterLayoutRegion = function(mgr, config){
46138     Roo.LayoutRegion.call(this, mgr, config, "center");
46139     this.visible = true;
46140     this.minWidth = config.minWidth || 20;
46141     this.minHeight = config.minHeight || 20;
46142 };
46143
46144 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46145     hide : function(){
46146         // center panel can't be hidden
46147     },
46148     
46149     show : function(){
46150         // center panel can't be hidden
46151     },
46152     
46153     getMinWidth: function(){
46154         return this.minWidth;
46155     },
46156     
46157     getMinHeight: function(){
46158         return this.minHeight;
46159     }
46160 });
46161
46162
46163 Roo.NorthLayoutRegion = function(mgr, config){
46164     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46165     if(this.split){
46166         this.split.placement = Roo.SplitBar.TOP;
46167         this.split.orientation = Roo.SplitBar.VERTICAL;
46168         this.split.el.addClass("x-layout-split-v");
46169     }
46170     var size = config.initialSize || config.height;
46171     if(typeof size != "undefined"){
46172         this.el.setHeight(size);
46173     }
46174 };
46175 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46176     orientation: Roo.SplitBar.VERTICAL,
46177     getBox : function(){
46178         if(this.collapsed){
46179             return this.collapsedEl.getBox();
46180         }
46181         var box = this.el.getBox();
46182         if(this.split){
46183             box.height += this.split.el.getHeight();
46184         }
46185         return box;
46186     },
46187     
46188     updateBox : function(box){
46189         if(this.split && !this.collapsed){
46190             box.height -= this.split.el.getHeight();
46191             this.split.el.setLeft(box.x);
46192             this.split.el.setTop(box.y+box.height);
46193             this.split.el.setWidth(box.width);
46194         }
46195         if(this.collapsed){
46196             this.updateBody(box.width, null);
46197         }
46198         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46199     }
46200 });
46201
46202 Roo.SouthLayoutRegion = function(mgr, config){
46203     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46204     if(this.split){
46205         this.split.placement = Roo.SplitBar.BOTTOM;
46206         this.split.orientation = Roo.SplitBar.VERTICAL;
46207         this.split.el.addClass("x-layout-split-v");
46208     }
46209     var size = config.initialSize || config.height;
46210     if(typeof size != "undefined"){
46211         this.el.setHeight(size);
46212     }
46213 };
46214 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46215     orientation: Roo.SplitBar.VERTICAL,
46216     getBox : function(){
46217         if(this.collapsed){
46218             return this.collapsedEl.getBox();
46219         }
46220         var box = this.el.getBox();
46221         if(this.split){
46222             var sh = this.split.el.getHeight();
46223             box.height += sh;
46224             box.y -= sh;
46225         }
46226         return box;
46227     },
46228     
46229     updateBox : function(box){
46230         if(this.split && !this.collapsed){
46231             var sh = this.split.el.getHeight();
46232             box.height -= sh;
46233             box.y += sh;
46234             this.split.el.setLeft(box.x);
46235             this.split.el.setTop(box.y-sh);
46236             this.split.el.setWidth(box.width);
46237         }
46238         if(this.collapsed){
46239             this.updateBody(box.width, null);
46240         }
46241         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46242     }
46243 });
46244
46245 Roo.EastLayoutRegion = function(mgr, config){
46246     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46247     if(this.split){
46248         this.split.placement = Roo.SplitBar.RIGHT;
46249         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46250         this.split.el.addClass("x-layout-split-h");
46251     }
46252     var size = config.initialSize || config.width;
46253     if(typeof size != "undefined"){
46254         this.el.setWidth(size);
46255     }
46256 };
46257 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46258     orientation: Roo.SplitBar.HORIZONTAL,
46259     getBox : function(){
46260         if(this.collapsed){
46261             return this.collapsedEl.getBox();
46262         }
46263         var box = this.el.getBox();
46264         if(this.split){
46265             var sw = this.split.el.getWidth();
46266             box.width += sw;
46267             box.x -= sw;
46268         }
46269         return box;
46270     },
46271
46272     updateBox : function(box){
46273         if(this.split && !this.collapsed){
46274             var sw = this.split.el.getWidth();
46275             box.width -= sw;
46276             this.split.el.setLeft(box.x);
46277             this.split.el.setTop(box.y);
46278             this.split.el.setHeight(box.height);
46279             box.x += sw;
46280         }
46281         if(this.collapsed){
46282             this.updateBody(null, box.height);
46283         }
46284         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46285     }
46286 });
46287
46288 Roo.WestLayoutRegion = function(mgr, config){
46289     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46290     if(this.split){
46291         this.split.placement = Roo.SplitBar.LEFT;
46292         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46293         this.split.el.addClass("x-layout-split-h");
46294     }
46295     var size = config.initialSize || config.width;
46296     if(typeof size != "undefined"){
46297         this.el.setWidth(size);
46298     }
46299 };
46300 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46301     orientation: Roo.SplitBar.HORIZONTAL,
46302     getBox : function(){
46303         if(this.collapsed){
46304             return this.collapsedEl.getBox();
46305         }
46306         var box = this.el.getBox();
46307         if(this.split){
46308             box.width += this.split.el.getWidth();
46309         }
46310         return box;
46311     },
46312     
46313     updateBox : function(box){
46314         if(this.split && !this.collapsed){
46315             var sw = this.split.el.getWidth();
46316             box.width -= sw;
46317             this.split.el.setLeft(box.x+box.width);
46318             this.split.el.setTop(box.y);
46319             this.split.el.setHeight(box.height);
46320         }
46321         if(this.collapsed){
46322             this.updateBody(null, box.height);
46323         }
46324         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46325     }
46326 });
46327 /*
46328  * Based on:
46329  * Ext JS Library 1.1.1
46330  * Copyright(c) 2006-2007, Ext JS, LLC.
46331  *
46332  * Originally Released Under LGPL - original licence link has changed is not relivant.
46333  *
46334  * Fork - LGPL
46335  * <script type="text/javascript">
46336  */
46337  
46338  
46339 /*
46340  * Private internal class for reading and applying state
46341  */
46342 Roo.LayoutStateManager = function(layout){
46343      // default empty state
46344      this.state = {
46345         north: {},
46346         south: {},
46347         east: {},
46348         west: {}       
46349     };
46350 };
46351
46352 Roo.LayoutStateManager.prototype = {
46353     init : function(layout, provider){
46354         this.provider = provider;
46355         var state = provider.get(layout.id+"-layout-state");
46356         if(state){
46357             var wasUpdating = layout.isUpdating();
46358             if(!wasUpdating){
46359                 layout.beginUpdate();
46360             }
46361             for(var key in state){
46362                 if(typeof state[key] != "function"){
46363                     var rstate = state[key];
46364                     var r = layout.getRegion(key);
46365                     if(r && rstate){
46366                         if(rstate.size){
46367                             r.resizeTo(rstate.size);
46368                         }
46369                         if(rstate.collapsed == true){
46370                             r.collapse(true);
46371                         }else{
46372                             r.expand(null, true);
46373                         }
46374                     }
46375                 }
46376             }
46377             if(!wasUpdating){
46378                 layout.endUpdate();
46379             }
46380             this.state = state; 
46381         }
46382         this.layout = layout;
46383         layout.on("regionresized", this.onRegionResized, this);
46384         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46385         layout.on("regionexpanded", this.onRegionExpanded, this);
46386     },
46387     
46388     storeState : function(){
46389         this.provider.set(this.layout.id+"-layout-state", this.state);
46390     },
46391     
46392     onRegionResized : function(region, newSize){
46393         this.state[region.getPosition()].size = newSize;
46394         this.storeState();
46395     },
46396     
46397     onRegionCollapsed : function(region){
46398         this.state[region.getPosition()].collapsed = true;
46399         this.storeState();
46400     },
46401     
46402     onRegionExpanded : function(region){
46403         this.state[region.getPosition()].collapsed = false;
46404         this.storeState();
46405     }
46406 };/*
46407  * Based on:
46408  * Ext JS Library 1.1.1
46409  * Copyright(c) 2006-2007, Ext JS, LLC.
46410  *
46411  * Originally Released Under LGPL - original licence link has changed is not relivant.
46412  *
46413  * Fork - LGPL
46414  * <script type="text/javascript">
46415  */
46416 /**
46417  * @class Roo.ContentPanel
46418  * @extends Roo.util.Observable
46419  * A basic ContentPanel element.
46420  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46421  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46422  * @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
46423  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46424  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46425  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46426  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46427  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46428  * @cfg {String} title          The title for this panel
46429  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46430  * @cfg {String} url            Calls {@link #setUrl} with this value
46431  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46432  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46433  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46434  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46435
46436  * @constructor
46437  * Create a new ContentPanel.
46438  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46439  * @param {String/Object} config A string to set only the title or a config object
46440  * @param {String} content (optional) Set the HTML content for this panel
46441  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46442  */
46443 Roo.ContentPanel = function(el, config, content){
46444     
46445      
46446     /*
46447     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46448         config = el;
46449         el = Roo.id();
46450     }
46451     if (config && config.parentLayout) { 
46452         el = config.parentLayout.el.createChild(); 
46453     }
46454     */
46455     if(el.autoCreate){ // xtype is available if this is called from factory
46456         config = el;
46457         el = Roo.id();
46458     }
46459     this.el = Roo.get(el);
46460     if(!this.el && config && config.autoCreate){
46461         if(typeof config.autoCreate == "object"){
46462             if(!config.autoCreate.id){
46463                 config.autoCreate.id = config.id||el;
46464             }
46465             this.el = Roo.DomHelper.append(document.body,
46466                         config.autoCreate, true);
46467         }else{
46468             this.el = Roo.DomHelper.append(document.body,
46469                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46470         }
46471     }
46472     this.closable = false;
46473     this.loaded = false;
46474     this.active = false;
46475     if(typeof config == "string"){
46476         this.title = config;
46477     }else{
46478         Roo.apply(this, config);
46479     }
46480     
46481     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46482         this.wrapEl = this.el.wrap();
46483         this.toolbar.container = this.el.insertSibling(false, 'before');
46484         this.toolbar = new Roo.Toolbar(this.toolbar);
46485     }
46486     
46487     
46488     
46489     if(this.resizeEl){
46490         this.resizeEl = Roo.get(this.resizeEl, true);
46491     }else{
46492         this.resizeEl = this.el;
46493     }
46494     this.addEvents({
46495         /**
46496          * @event activate
46497          * Fires when this panel is activated. 
46498          * @param {Roo.ContentPanel} this
46499          */
46500         "activate" : true,
46501         /**
46502          * @event deactivate
46503          * Fires when this panel is activated. 
46504          * @param {Roo.ContentPanel} this
46505          */
46506         "deactivate" : true,
46507
46508         /**
46509          * @event resize
46510          * Fires when this panel is resized if fitToFrame is true.
46511          * @param {Roo.ContentPanel} this
46512          * @param {Number} width The width after any component adjustments
46513          * @param {Number} height The height after any component adjustments
46514          */
46515         "resize" : true,
46516         
46517          /**
46518          * @event render
46519          * Fires when this tab is created
46520          * @param {Roo.ContentPanel} this
46521          */
46522         "render" : true
46523         
46524         
46525         
46526     });
46527     if(this.autoScroll){
46528         this.resizeEl.setStyle("overflow", "auto");
46529     } else {
46530         // fix randome scrolling
46531         this.el.on('scroll', function() {
46532             Roo.log('fix random scolling');
46533             this.scrollTo('top',0); 
46534         });
46535     }
46536     content = content || this.content;
46537     if(content){
46538         this.setContent(content);
46539     }
46540     if(config && config.url){
46541         this.setUrl(this.url, this.params, this.loadOnce);
46542     }
46543     
46544     
46545     
46546     Roo.ContentPanel.superclass.constructor.call(this);
46547     
46548     this.fireEvent('render', this);
46549 };
46550
46551 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46552     tabTip:'',
46553     setRegion : function(region){
46554         this.region = region;
46555         if(region){
46556            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46557         }else{
46558            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46559         } 
46560     },
46561     
46562     /**
46563      * Returns the toolbar for this Panel if one was configured. 
46564      * @return {Roo.Toolbar} 
46565      */
46566     getToolbar : function(){
46567         return this.toolbar;
46568     },
46569     
46570     setActiveState : function(active){
46571         this.active = active;
46572         if(!active){
46573             this.fireEvent("deactivate", this);
46574         }else{
46575             this.fireEvent("activate", this);
46576         }
46577     },
46578     /**
46579      * Updates this panel's element
46580      * @param {String} content The new content
46581      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46582     */
46583     setContent : function(content, loadScripts){
46584         this.el.update(content, loadScripts);
46585     },
46586
46587     ignoreResize : function(w, h){
46588         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46589             return true;
46590         }else{
46591             this.lastSize = {width: w, height: h};
46592             return false;
46593         }
46594     },
46595     /**
46596      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46597      * @return {Roo.UpdateManager} The UpdateManager
46598      */
46599     getUpdateManager : function(){
46600         return this.el.getUpdateManager();
46601     },
46602      /**
46603      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46604      * @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:
46605 <pre><code>
46606 panel.load({
46607     url: "your-url.php",
46608     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46609     callback: yourFunction,
46610     scope: yourObject, //(optional scope)
46611     discardUrl: false,
46612     nocache: false,
46613     text: "Loading...",
46614     timeout: 30,
46615     scripts: false
46616 });
46617 </code></pre>
46618      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46619      * 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.
46620      * @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}
46621      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46622      * @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.
46623      * @return {Roo.ContentPanel} this
46624      */
46625     load : function(){
46626         var um = this.el.getUpdateManager();
46627         um.update.apply(um, arguments);
46628         return this;
46629     },
46630
46631
46632     /**
46633      * 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.
46634      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46635      * @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)
46636      * @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)
46637      * @return {Roo.UpdateManager} The UpdateManager
46638      */
46639     setUrl : function(url, params, loadOnce){
46640         if(this.refreshDelegate){
46641             this.removeListener("activate", this.refreshDelegate);
46642         }
46643         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46644         this.on("activate", this.refreshDelegate);
46645         return this.el.getUpdateManager();
46646     },
46647     
46648     _handleRefresh : function(url, params, loadOnce){
46649         if(!loadOnce || !this.loaded){
46650             var updater = this.el.getUpdateManager();
46651             updater.update(url, params, this._setLoaded.createDelegate(this));
46652         }
46653     },
46654     
46655     _setLoaded : function(){
46656         this.loaded = true;
46657     }, 
46658     
46659     /**
46660      * Returns this panel's id
46661      * @return {String} 
46662      */
46663     getId : function(){
46664         return this.el.id;
46665     },
46666     
46667     /** 
46668      * Returns this panel's element - used by regiosn to add.
46669      * @return {Roo.Element} 
46670      */
46671     getEl : function(){
46672         return this.wrapEl || this.el;
46673     },
46674     
46675     adjustForComponents : function(width, height){
46676         if(this.resizeEl != this.el){
46677             width -= this.el.getFrameWidth('lr');
46678             height -= this.el.getFrameWidth('tb');
46679         }
46680         if(this.toolbar){
46681             var te = this.toolbar.getEl();
46682             height -= te.getHeight();
46683             te.setWidth(width);
46684         }
46685         if(this.adjustments){
46686             width += this.adjustments[0];
46687             height += this.adjustments[1];
46688         }
46689         return {"width": width, "height": height};
46690     },
46691     
46692     setSize : function(width, height){
46693         if(this.fitToFrame && !this.ignoreResize(width, height)){
46694             if(this.fitContainer && this.resizeEl != this.el){
46695                 this.el.setSize(width, height);
46696             }
46697             var size = this.adjustForComponents(width, height);
46698             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46699             this.fireEvent('resize', this, size.width, size.height);
46700         }
46701     },
46702     
46703     /**
46704      * Returns this panel's title
46705      * @return {String} 
46706      */
46707     getTitle : function(){
46708         return this.title;
46709     },
46710     
46711     /**
46712      * Set this panel's title
46713      * @param {String} title
46714      */
46715     setTitle : function(title){
46716         this.title = title;
46717         if(this.region){
46718             this.region.updatePanelTitle(this, title);
46719         }
46720     },
46721     
46722     /**
46723      * Returns true is this panel was configured to be closable
46724      * @return {Boolean} 
46725      */
46726     isClosable : function(){
46727         return this.closable;
46728     },
46729     
46730     beforeSlide : function(){
46731         this.el.clip();
46732         this.resizeEl.clip();
46733     },
46734     
46735     afterSlide : function(){
46736         this.el.unclip();
46737         this.resizeEl.unclip();
46738     },
46739     
46740     /**
46741      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46742      *   Will fail silently if the {@link #setUrl} method has not been called.
46743      *   This does not activate the panel, just updates its content.
46744      */
46745     refresh : function(){
46746         if(this.refreshDelegate){
46747            this.loaded = false;
46748            this.refreshDelegate();
46749         }
46750     },
46751     
46752     /**
46753      * Destroys this panel
46754      */
46755     destroy : function(){
46756         this.el.removeAllListeners();
46757         var tempEl = document.createElement("span");
46758         tempEl.appendChild(this.el.dom);
46759         tempEl.innerHTML = "";
46760         this.el.remove();
46761         this.el = null;
46762     },
46763     
46764     /**
46765      * form - if the content panel contains a form - this is a reference to it.
46766      * @type {Roo.form.Form}
46767      */
46768     form : false,
46769     /**
46770      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46771      *    This contains a reference to it.
46772      * @type {Roo.View}
46773      */
46774     view : false,
46775     
46776       /**
46777      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46778      * <pre><code>
46779
46780 layout.addxtype({
46781        xtype : 'Form',
46782        items: [ .... ]
46783    }
46784 );
46785
46786 </code></pre>
46787      * @param {Object} cfg Xtype definition of item to add.
46788      */
46789     
46790     addxtype : function(cfg) {
46791         // add form..
46792         if (cfg.xtype.match(/^Form$/)) {
46793             var el = this.el.createChild();
46794
46795             this.form = new  Roo.form.Form(cfg);
46796             
46797             
46798             if ( this.form.allItems.length) this.form.render(el.dom);
46799             return this.form;
46800         }
46801         // should only have one of theses..
46802         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46803             // views..
46804             cfg.el = this.el.appendChild(document.createElement("div"));
46805             // factory?
46806             
46807             var ret = new Roo.factory(cfg);
46808             ret.render && ret.render(false, ''); // render blank..
46809             this.view = ret;
46810             return ret;
46811         }
46812         return false;
46813     }
46814 });
46815
46816 /**
46817  * @class Roo.GridPanel
46818  * @extends Roo.ContentPanel
46819  * @constructor
46820  * Create a new GridPanel.
46821  * @param {Roo.grid.Grid} grid The grid for this panel
46822  * @param {String/Object} config A string to set only the panel's title, or a config object
46823  */
46824 Roo.GridPanel = function(grid, config){
46825     
46826   
46827     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46828         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46829         
46830     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46831     
46832     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46833     
46834     if(this.toolbar){
46835         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46836     }
46837     // xtype created footer. - not sure if will work as we normally have to render first..
46838     if (this.footer && !this.footer.el && this.footer.xtype) {
46839         
46840         this.footer.container = this.grid.getView().getFooterPanel(true);
46841         this.footer.dataSource = this.grid.dataSource;
46842         this.footer = Roo.factory(this.footer, Roo);
46843         
46844     }
46845     
46846     grid.monitorWindowResize = false; // turn off autosizing
46847     grid.autoHeight = false;
46848     grid.autoWidth = false;
46849     this.grid = grid;
46850     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46851 };
46852
46853 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46854     getId : function(){
46855         return this.grid.id;
46856     },
46857     
46858     /**
46859      * Returns the grid for this panel
46860      * @return {Roo.grid.Grid} 
46861      */
46862     getGrid : function(){
46863         return this.grid;    
46864     },
46865     
46866     setSize : function(width, height){
46867         if(!this.ignoreResize(width, height)){
46868             var grid = this.grid;
46869             var size = this.adjustForComponents(width, height);
46870             grid.getGridEl().setSize(size.width, size.height);
46871             grid.autoSize();
46872         }
46873     },
46874     
46875     beforeSlide : function(){
46876         this.grid.getView().scroller.clip();
46877     },
46878     
46879     afterSlide : function(){
46880         this.grid.getView().scroller.unclip();
46881     },
46882     
46883     destroy : function(){
46884         this.grid.destroy();
46885         delete this.grid;
46886         Roo.GridPanel.superclass.destroy.call(this); 
46887     }
46888 });
46889
46890
46891 /**
46892  * @class Roo.NestedLayoutPanel
46893  * @extends Roo.ContentPanel
46894  * @constructor
46895  * Create a new NestedLayoutPanel.
46896  * 
46897  * 
46898  * @param {Roo.BorderLayout} layout The layout for this panel
46899  * @param {String/Object} config A string to set only the title or a config object
46900  */
46901 Roo.NestedLayoutPanel = function(layout, config)
46902 {
46903     // construct with only one argument..
46904     /* FIXME - implement nicer consturctors
46905     if (layout.layout) {
46906         config = layout;
46907         layout = config.layout;
46908         delete config.layout;
46909     }
46910     if (layout.xtype && !layout.getEl) {
46911         // then layout needs constructing..
46912         layout = Roo.factory(layout, Roo);
46913     }
46914     */
46915     
46916     
46917     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46918     
46919     layout.monitorWindowResize = false; // turn off autosizing
46920     this.layout = layout;
46921     this.layout.getEl().addClass("x-layout-nested-layout");
46922     
46923     
46924     
46925     
46926 };
46927
46928 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46929
46930     setSize : function(width, height){
46931         if(!this.ignoreResize(width, height)){
46932             var size = this.adjustForComponents(width, height);
46933             var el = this.layout.getEl();
46934             el.setSize(size.width, size.height);
46935             var touch = el.dom.offsetWidth;
46936             this.layout.layout();
46937             // ie requires a double layout on the first pass
46938             if(Roo.isIE && !this.initialized){
46939                 this.initialized = true;
46940                 this.layout.layout();
46941             }
46942         }
46943     },
46944     
46945     // activate all subpanels if not currently active..
46946     
46947     setActiveState : function(active){
46948         this.active = active;
46949         if(!active){
46950             this.fireEvent("deactivate", this);
46951             return;
46952         }
46953         
46954         this.fireEvent("activate", this);
46955         // not sure if this should happen before or after..
46956         if (!this.layout) {
46957             return; // should not happen..
46958         }
46959         var reg = false;
46960         for (var r in this.layout.regions) {
46961             reg = this.layout.getRegion(r);
46962             if (reg.getActivePanel()) {
46963                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46964                 reg.setActivePanel(reg.getActivePanel());
46965                 continue;
46966             }
46967             if (!reg.panels.length) {
46968                 continue;
46969             }
46970             reg.showPanel(reg.getPanel(0));
46971         }
46972         
46973         
46974         
46975         
46976     },
46977     
46978     /**
46979      * Returns the nested BorderLayout for this panel
46980      * @return {Roo.BorderLayout} 
46981      */
46982     getLayout : function(){
46983         return this.layout;
46984     },
46985     
46986      /**
46987      * Adds a xtype elements to the layout of the nested panel
46988      * <pre><code>
46989
46990 panel.addxtype({
46991        xtype : 'ContentPanel',
46992        region: 'west',
46993        items: [ .... ]
46994    }
46995 );
46996
46997 panel.addxtype({
46998         xtype : 'NestedLayoutPanel',
46999         region: 'west',
47000         layout: {
47001            center: { },
47002            west: { }   
47003         },
47004         items : [ ... list of content panels or nested layout panels.. ]
47005    }
47006 );
47007 </code></pre>
47008      * @param {Object} cfg Xtype definition of item to add.
47009      */
47010     addxtype : function(cfg) {
47011         return this.layout.addxtype(cfg);
47012     
47013     }
47014 });
47015
47016 Roo.ScrollPanel = function(el, config, content){
47017     config = config || {};
47018     config.fitToFrame = true;
47019     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47020     
47021     this.el.dom.style.overflow = "hidden";
47022     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47023     this.el.removeClass("x-layout-inactive-content");
47024     this.el.on("mousewheel", this.onWheel, this);
47025
47026     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47027     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47028     up.unselectable(); down.unselectable();
47029     up.on("click", this.scrollUp, this);
47030     down.on("click", this.scrollDown, this);
47031     up.addClassOnOver("x-scroller-btn-over");
47032     down.addClassOnOver("x-scroller-btn-over");
47033     up.addClassOnClick("x-scroller-btn-click");
47034     down.addClassOnClick("x-scroller-btn-click");
47035     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47036
47037     this.resizeEl = this.el;
47038     this.el = wrap; this.up = up; this.down = down;
47039 };
47040
47041 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47042     increment : 100,
47043     wheelIncrement : 5,
47044     scrollUp : function(){
47045         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47046     },
47047
47048     scrollDown : function(){
47049         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47050     },
47051
47052     afterScroll : function(){
47053         var el = this.resizeEl;
47054         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47055         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47056         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47057     },
47058
47059     setSize : function(){
47060         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47061         this.afterScroll();
47062     },
47063
47064     onWheel : function(e){
47065         var d = e.getWheelDelta();
47066         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47067         this.afterScroll();
47068         e.stopEvent();
47069     },
47070
47071     setContent : function(content, loadScripts){
47072         this.resizeEl.update(content, loadScripts);
47073     }
47074
47075 });
47076
47077
47078
47079
47080
47081
47082
47083
47084
47085 /**
47086  * @class Roo.TreePanel
47087  * @extends Roo.ContentPanel
47088  * @constructor
47089  * Create a new TreePanel. - defaults to fit/scoll contents.
47090  * @param {String/Object} config A string to set only the panel's title, or a config object
47091  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47092  */
47093 Roo.TreePanel = function(config){
47094     var el = config.el;
47095     var tree = config.tree;
47096     delete config.tree; 
47097     delete config.el; // hopefull!
47098     
47099     // wrapper for IE7 strict & safari scroll issue
47100     
47101     var treeEl = el.createChild();
47102     config.resizeEl = treeEl;
47103     
47104     
47105     
47106     Roo.TreePanel.superclass.constructor.call(this, el, config);
47107  
47108  
47109     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47110     //console.log(tree);
47111     this.on('activate', function()
47112     {
47113         if (this.tree.rendered) {
47114             return;
47115         }
47116         //console.log('render tree');
47117         this.tree.render();
47118     });
47119     
47120     this.on('resize',  function (cp, w, h) {
47121             this.tree.innerCt.setWidth(w);
47122             this.tree.innerCt.setHeight(h);
47123             this.tree.innerCt.setStyle('overflow-y', 'auto');
47124     });
47125
47126         
47127     
47128 };
47129
47130 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47131     fitToFrame : true,
47132     autoScroll : true
47133 });
47134
47135
47136
47137
47138
47139
47140
47141
47142
47143
47144
47145 /*
47146  * Based on:
47147  * Ext JS Library 1.1.1
47148  * Copyright(c) 2006-2007, Ext JS, LLC.
47149  *
47150  * Originally Released Under LGPL - original licence link has changed is not relivant.
47151  *
47152  * Fork - LGPL
47153  * <script type="text/javascript">
47154  */
47155  
47156
47157 /**
47158  * @class Roo.ReaderLayout
47159  * @extends Roo.BorderLayout
47160  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47161  * center region containing two nested regions (a top one for a list view and one for item preview below),
47162  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47163  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47164  * expedites the setup of the overall layout and regions for this common application style.
47165  * Example:
47166  <pre><code>
47167 var reader = new Roo.ReaderLayout();
47168 var CP = Roo.ContentPanel;  // shortcut for adding
47169
47170 reader.beginUpdate();
47171 reader.add("north", new CP("north", "North"));
47172 reader.add("west", new CP("west", {title: "West"}));
47173 reader.add("east", new CP("east", {title: "East"}));
47174
47175 reader.regions.listView.add(new CP("listView", "List"));
47176 reader.regions.preview.add(new CP("preview", "Preview"));
47177 reader.endUpdate();
47178 </code></pre>
47179 * @constructor
47180 * Create a new ReaderLayout
47181 * @param {Object} config Configuration options
47182 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47183 * document.body if omitted)
47184 */
47185 Roo.ReaderLayout = function(config, renderTo){
47186     var c = config || {size:{}};
47187     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47188         north: c.north !== false ? Roo.apply({
47189             split:false,
47190             initialSize: 32,
47191             titlebar: false
47192         }, c.north) : false,
47193         west: c.west !== false ? Roo.apply({
47194             split:true,
47195             initialSize: 200,
47196             minSize: 175,
47197             maxSize: 400,
47198             titlebar: true,
47199             collapsible: true,
47200             animate: true,
47201             margins:{left:5,right:0,bottom:5,top:5},
47202             cmargins:{left:5,right:5,bottom:5,top:5}
47203         }, c.west) : false,
47204         east: c.east !== false ? Roo.apply({
47205             split:true,
47206             initialSize: 200,
47207             minSize: 175,
47208             maxSize: 400,
47209             titlebar: true,
47210             collapsible: true,
47211             animate: true,
47212             margins:{left:0,right:5,bottom:5,top:5},
47213             cmargins:{left:5,right:5,bottom:5,top:5}
47214         }, c.east) : false,
47215         center: Roo.apply({
47216             tabPosition: 'top',
47217             autoScroll:false,
47218             closeOnTab: true,
47219             titlebar:false,
47220             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47221         }, c.center)
47222     });
47223
47224     this.el.addClass('x-reader');
47225
47226     this.beginUpdate();
47227
47228     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47229         south: c.preview !== false ? Roo.apply({
47230             split:true,
47231             initialSize: 200,
47232             minSize: 100,
47233             autoScroll:true,
47234             collapsible:true,
47235             titlebar: true,
47236             cmargins:{top:5,left:0, right:0, bottom:0}
47237         }, c.preview) : false,
47238         center: Roo.apply({
47239             autoScroll:false,
47240             titlebar:false,
47241             minHeight:200
47242         }, c.listView)
47243     });
47244     this.add('center', new Roo.NestedLayoutPanel(inner,
47245             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47246
47247     this.endUpdate();
47248
47249     this.regions.preview = inner.getRegion('south');
47250     this.regions.listView = inner.getRegion('center');
47251 };
47252
47253 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47254  * Based on:
47255  * Ext JS Library 1.1.1
47256  * Copyright(c) 2006-2007, Ext JS, LLC.
47257  *
47258  * Originally Released Under LGPL - original licence link has changed is not relivant.
47259  *
47260  * Fork - LGPL
47261  * <script type="text/javascript">
47262  */
47263  
47264 /**
47265  * @class Roo.grid.Grid
47266  * @extends Roo.util.Observable
47267  * This class represents the primary interface of a component based grid control.
47268  * <br><br>Usage:<pre><code>
47269  var grid = new Roo.grid.Grid("my-container-id", {
47270      ds: myDataStore,
47271      cm: myColModel,
47272      selModel: mySelectionModel,
47273      autoSizeColumns: true,
47274      monitorWindowResize: false,
47275      trackMouseOver: true
47276  });
47277  // set any options
47278  grid.render();
47279  * </code></pre>
47280  * <b>Common Problems:</b><br/>
47281  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47282  * element will correct this<br/>
47283  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47284  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47285  * are unpredictable.<br/>
47286  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47287  * grid to calculate dimensions/offsets.<br/>
47288   * @constructor
47289  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47290  * The container MUST have some type of size defined for the grid to fill. The container will be
47291  * automatically set to position relative if it isn't already.
47292  * @param {Object} config A config object that sets properties on this grid.
47293  */
47294 Roo.grid.Grid = function(container, config){
47295         // initialize the container
47296         this.container = Roo.get(container);
47297         this.container.update("");
47298         this.container.setStyle("overflow", "hidden");
47299     this.container.addClass('x-grid-container');
47300
47301     this.id = this.container.id;
47302
47303     Roo.apply(this, config);
47304     // check and correct shorthanded configs
47305     if(this.ds){
47306         this.dataSource = this.ds;
47307         delete this.ds;
47308     }
47309     if(this.cm){
47310         this.colModel = this.cm;
47311         delete this.cm;
47312     }
47313     if(this.sm){
47314         this.selModel = this.sm;
47315         delete this.sm;
47316     }
47317
47318     if (this.selModel) {
47319         this.selModel = Roo.factory(this.selModel, Roo.grid);
47320         this.sm = this.selModel;
47321         this.sm.xmodule = this.xmodule || false;
47322     }
47323     if (typeof(this.colModel.config) == 'undefined') {
47324         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47325         this.cm = this.colModel;
47326         this.cm.xmodule = this.xmodule || false;
47327     }
47328     if (this.dataSource) {
47329         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47330         this.ds = this.dataSource;
47331         this.ds.xmodule = this.xmodule || false;
47332          
47333     }
47334     
47335     
47336     
47337     if(this.width){
47338         this.container.setWidth(this.width);
47339     }
47340
47341     if(this.height){
47342         this.container.setHeight(this.height);
47343     }
47344     /** @private */
47345         this.addEvents({
47346         // raw events
47347         /**
47348          * @event click
47349          * The raw click event for the entire grid.
47350          * @param {Roo.EventObject} e
47351          */
47352         "click" : true,
47353         /**
47354          * @event dblclick
47355          * The raw dblclick event for the entire grid.
47356          * @param {Roo.EventObject} e
47357          */
47358         "dblclick" : true,
47359         /**
47360          * @event contextmenu
47361          * The raw contextmenu event for the entire grid.
47362          * @param {Roo.EventObject} e
47363          */
47364         "contextmenu" : true,
47365         /**
47366          * @event mousedown
47367          * The raw mousedown event for the entire grid.
47368          * @param {Roo.EventObject} e
47369          */
47370         "mousedown" : true,
47371         /**
47372          * @event mouseup
47373          * The raw mouseup event for the entire grid.
47374          * @param {Roo.EventObject} e
47375          */
47376         "mouseup" : true,
47377         /**
47378          * @event mouseover
47379          * The raw mouseover event for the entire grid.
47380          * @param {Roo.EventObject} e
47381          */
47382         "mouseover" : true,
47383         /**
47384          * @event mouseout
47385          * The raw mouseout event for the entire grid.
47386          * @param {Roo.EventObject} e
47387          */
47388         "mouseout" : true,
47389         /**
47390          * @event keypress
47391          * The raw keypress event for the entire grid.
47392          * @param {Roo.EventObject} e
47393          */
47394         "keypress" : true,
47395         /**
47396          * @event keydown
47397          * The raw keydown event for the entire grid.
47398          * @param {Roo.EventObject} e
47399          */
47400         "keydown" : true,
47401
47402         // custom events
47403
47404         /**
47405          * @event cellclick
47406          * Fires when a cell is clicked
47407          * @param {Grid} this
47408          * @param {Number} rowIndex
47409          * @param {Number} columnIndex
47410          * @param {Roo.EventObject} e
47411          */
47412         "cellclick" : true,
47413         /**
47414          * @event celldblclick
47415          * Fires when a cell is double clicked
47416          * @param {Grid} this
47417          * @param {Number} rowIndex
47418          * @param {Number} columnIndex
47419          * @param {Roo.EventObject} e
47420          */
47421         "celldblclick" : true,
47422         /**
47423          * @event rowclick
47424          * Fires when a row is clicked
47425          * @param {Grid} this
47426          * @param {Number} rowIndex
47427          * @param {Roo.EventObject} e
47428          */
47429         "rowclick" : true,
47430         /**
47431          * @event rowdblclick
47432          * Fires when a row is double clicked
47433          * @param {Grid} this
47434          * @param {Number} rowIndex
47435          * @param {Roo.EventObject} e
47436          */
47437         "rowdblclick" : true,
47438         /**
47439          * @event headerclick
47440          * Fires when a header is clicked
47441          * @param {Grid} this
47442          * @param {Number} columnIndex
47443          * @param {Roo.EventObject} e
47444          */
47445         "headerclick" : true,
47446         /**
47447          * @event headerdblclick
47448          * Fires when a header cell is double clicked
47449          * @param {Grid} this
47450          * @param {Number} columnIndex
47451          * @param {Roo.EventObject} e
47452          */
47453         "headerdblclick" : true,
47454         /**
47455          * @event rowcontextmenu
47456          * Fires when a row is right clicked
47457          * @param {Grid} this
47458          * @param {Number} rowIndex
47459          * @param {Roo.EventObject} e
47460          */
47461         "rowcontextmenu" : true,
47462         /**
47463          * @event cellcontextmenu
47464          * Fires when a cell is right clicked
47465          * @param {Grid} this
47466          * @param {Number} rowIndex
47467          * @param {Number} cellIndex
47468          * @param {Roo.EventObject} e
47469          */
47470          "cellcontextmenu" : true,
47471         /**
47472          * @event headercontextmenu
47473          * Fires when a header is right clicked
47474          * @param {Grid} this
47475          * @param {Number} columnIndex
47476          * @param {Roo.EventObject} e
47477          */
47478         "headercontextmenu" : true,
47479         /**
47480          * @event bodyscroll
47481          * Fires when the body element is scrolled
47482          * @param {Number} scrollLeft
47483          * @param {Number} scrollTop
47484          */
47485         "bodyscroll" : true,
47486         /**
47487          * @event columnresize
47488          * Fires when the user resizes a column
47489          * @param {Number} columnIndex
47490          * @param {Number} newSize
47491          */
47492         "columnresize" : true,
47493         /**
47494          * @event columnmove
47495          * Fires when the user moves a column
47496          * @param {Number} oldIndex
47497          * @param {Number} newIndex
47498          */
47499         "columnmove" : true,
47500         /**
47501          * @event startdrag
47502          * Fires when row(s) start being dragged
47503          * @param {Grid} this
47504          * @param {Roo.GridDD} dd The drag drop object
47505          * @param {event} e The raw browser event
47506          */
47507         "startdrag" : true,
47508         /**
47509          * @event enddrag
47510          * Fires when a drag operation is complete
47511          * @param {Grid} this
47512          * @param {Roo.GridDD} dd The drag drop object
47513          * @param {event} e The raw browser event
47514          */
47515         "enddrag" : true,
47516         /**
47517          * @event dragdrop
47518          * Fires when dragged row(s) are dropped on a valid DD target
47519          * @param {Grid} this
47520          * @param {Roo.GridDD} dd The drag drop object
47521          * @param {String} targetId The target drag drop object
47522          * @param {event} e The raw browser event
47523          */
47524         "dragdrop" : true,
47525         /**
47526          * @event dragover
47527          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47528          * @param {Grid} this
47529          * @param {Roo.GridDD} dd The drag drop object
47530          * @param {String} targetId The target drag drop object
47531          * @param {event} e The raw browser event
47532          */
47533         "dragover" : true,
47534         /**
47535          * @event dragenter
47536          *  Fires when the dragged row(s) first cross another DD target while being dragged
47537          * @param {Grid} this
47538          * @param {Roo.GridDD} dd The drag drop object
47539          * @param {String} targetId The target drag drop object
47540          * @param {event} e The raw browser event
47541          */
47542         "dragenter" : true,
47543         /**
47544          * @event dragout
47545          * Fires when the dragged row(s) leave another DD target while being dragged
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         "dragout" : true,
47552         /**
47553          * @event rowclass
47554          * Fires when a row is rendered, so you can change add a style to it.
47555          * @param {GridView} gridview   The grid view
47556          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47557          */
47558         'rowclass' : true,
47559
47560         /**
47561          * @event render
47562          * Fires when the grid is rendered
47563          * @param {Grid} grid
47564          */
47565         'render' : true
47566     });
47567
47568     Roo.grid.Grid.superclass.constructor.call(this);
47569 };
47570 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47571     
47572     /**
47573      * @cfg {String} ddGroup - drag drop group.
47574      */
47575
47576     /**
47577      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47578      */
47579     minColumnWidth : 25,
47580
47581     /**
47582      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47583      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47584      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47585      */
47586     autoSizeColumns : false,
47587
47588     /**
47589      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47590      */
47591     autoSizeHeaders : true,
47592
47593     /**
47594      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47595      */
47596     monitorWindowResize : true,
47597
47598     /**
47599      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47600      * rows measured to get a columns size. Default is 0 (all rows).
47601      */
47602     maxRowsToMeasure : 0,
47603
47604     /**
47605      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47606      */
47607     trackMouseOver : true,
47608
47609     /**
47610     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47611     */
47612     
47613     /**
47614     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47615     */
47616     enableDragDrop : false,
47617     
47618     /**
47619     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47620     */
47621     enableColumnMove : true,
47622     
47623     /**
47624     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47625     */
47626     enableColumnHide : true,
47627     
47628     /**
47629     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47630     */
47631     enableRowHeightSync : false,
47632     
47633     /**
47634     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47635     */
47636     stripeRows : true,
47637     
47638     /**
47639     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47640     */
47641     autoHeight : false,
47642
47643     /**
47644      * @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.
47645      */
47646     autoExpandColumn : false,
47647
47648     /**
47649     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47650     * Default is 50.
47651     */
47652     autoExpandMin : 50,
47653
47654     /**
47655     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47656     */
47657     autoExpandMax : 1000,
47658
47659     /**
47660     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47661     */
47662     view : null,
47663
47664     /**
47665     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47666     */
47667     loadMask : false,
47668     /**
47669     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47670     */
47671     dropTarget: false,
47672     
47673    
47674     
47675     // private
47676     rendered : false,
47677
47678     /**
47679     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47680     * of a fixed width. Default is false.
47681     */
47682     /**
47683     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47684     */
47685     /**
47686      * Called once after all setup has been completed and the grid is ready to be rendered.
47687      * @return {Roo.grid.Grid} this
47688      */
47689     render : function()
47690     {
47691         var c = this.container;
47692         // try to detect autoHeight/width mode
47693         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47694             this.autoHeight = true;
47695         }
47696         var view = this.getView();
47697         view.init(this);
47698
47699         c.on("click", this.onClick, this);
47700         c.on("dblclick", this.onDblClick, this);
47701         c.on("contextmenu", this.onContextMenu, this);
47702         c.on("keydown", this.onKeyDown, this);
47703
47704         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47705
47706         this.getSelectionModel().init(this);
47707
47708         view.render();
47709
47710         if(this.loadMask){
47711             this.loadMask = new Roo.LoadMask(this.container,
47712                     Roo.apply({store:this.dataSource}, this.loadMask));
47713         }
47714         
47715         
47716         if (this.toolbar && this.toolbar.xtype) {
47717             this.toolbar.container = this.getView().getHeaderPanel(true);
47718             this.toolbar = new Roo.Toolbar(this.toolbar);
47719         }
47720         if (this.footer && this.footer.xtype) {
47721             this.footer.dataSource = this.getDataSource();
47722             this.footer.container = this.getView().getFooterPanel(true);
47723             this.footer = Roo.factory(this.footer, Roo);
47724         }
47725         if (this.dropTarget && this.dropTarget.xtype) {
47726             delete this.dropTarget.xtype;
47727             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47728         }
47729         
47730         
47731         this.rendered = true;
47732         this.fireEvent('render', this);
47733         return this;
47734     },
47735
47736         /**
47737          * Reconfigures the grid to use a different Store and Column Model.
47738          * The View will be bound to the new objects and refreshed.
47739          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47740          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47741          */
47742     reconfigure : function(dataSource, colModel){
47743         if(this.loadMask){
47744             this.loadMask.destroy();
47745             this.loadMask = new Roo.LoadMask(this.container,
47746                     Roo.apply({store:dataSource}, this.loadMask));
47747         }
47748         this.view.bind(dataSource, colModel);
47749         this.dataSource = dataSource;
47750         this.colModel = colModel;
47751         this.view.refresh(true);
47752     },
47753
47754     // private
47755     onKeyDown : function(e){
47756         this.fireEvent("keydown", e);
47757     },
47758
47759     /**
47760      * Destroy this grid.
47761      * @param {Boolean} removeEl True to remove the element
47762      */
47763     destroy : function(removeEl, keepListeners){
47764         if(this.loadMask){
47765             this.loadMask.destroy();
47766         }
47767         var c = this.container;
47768         c.removeAllListeners();
47769         this.view.destroy();
47770         this.colModel.purgeListeners();
47771         if(!keepListeners){
47772             this.purgeListeners();
47773         }
47774         c.update("");
47775         if(removeEl === true){
47776             c.remove();
47777         }
47778     },
47779
47780     // private
47781     processEvent : function(name, e){
47782         this.fireEvent(name, e);
47783         var t = e.getTarget();
47784         var v = this.view;
47785         var header = v.findHeaderIndex(t);
47786         if(header !== false){
47787             this.fireEvent("header" + name, this, header, e);
47788         }else{
47789             var row = v.findRowIndex(t);
47790             var cell = v.findCellIndex(t);
47791             if(row !== false){
47792                 this.fireEvent("row" + name, this, row, e);
47793                 if(cell !== false){
47794                     this.fireEvent("cell" + name, this, row, cell, e);
47795                 }
47796             }
47797         }
47798     },
47799
47800     // private
47801     onClick : function(e){
47802         this.processEvent("click", e);
47803     },
47804
47805     // private
47806     onContextMenu : function(e, t){
47807         this.processEvent("contextmenu", e);
47808     },
47809
47810     // private
47811     onDblClick : function(e){
47812         this.processEvent("dblclick", e);
47813     },
47814
47815     // private
47816     walkCells : function(row, col, step, fn, scope){
47817         var cm = this.colModel, clen = cm.getColumnCount();
47818         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47819         if(step < 0){
47820             if(col < 0){
47821                 row--;
47822                 first = false;
47823             }
47824             while(row >= 0){
47825                 if(!first){
47826                     col = clen-1;
47827                 }
47828                 first = false;
47829                 while(col >= 0){
47830                     if(fn.call(scope || this, row, col, cm) === true){
47831                         return [row, col];
47832                     }
47833                     col--;
47834                 }
47835                 row--;
47836             }
47837         } else {
47838             if(col >= clen){
47839                 row++;
47840                 first = false;
47841             }
47842             while(row < rlen){
47843                 if(!first){
47844                     col = 0;
47845                 }
47846                 first = false;
47847                 while(col < clen){
47848                     if(fn.call(scope || this, row, col, cm) === true){
47849                         return [row, col];
47850                     }
47851                     col++;
47852                 }
47853                 row++;
47854             }
47855         }
47856         return null;
47857     },
47858
47859     // private
47860     getSelections : function(){
47861         return this.selModel.getSelections();
47862     },
47863
47864     /**
47865      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47866      * but if manual update is required this method will initiate it.
47867      */
47868     autoSize : function(){
47869         if(this.rendered){
47870             this.view.layout();
47871             if(this.view.adjustForScroll){
47872                 this.view.adjustForScroll();
47873             }
47874         }
47875     },
47876
47877     /**
47878      * Returns the grid's underlying element.
47879      * @return {Element} The element
47880      */
47881     getGridEl : function(){
47882         return this.container;
47883     },
47884
47885     // private for compatibility, overridden by editor grid
47886     stopEditing : function(){},
47887
47888     /**
47889      * Returns the grid's SelectionModel.
47890      * @return {SelectionModel}
47891      */
47892     getSelectionModel : function(){
47893         if(!this.selModel){
47894             this.selModel = new Roo.grid.RowSelectionModel();
47895         }
47896         return this.selModel;
47897     },
47898
47899     /**
47900      * Returns the grid's DataSource.
47901      * @return {DataSource}
47902      */
47903     getDataSource : function(){
47904         return this.dataSource;
47905     },
47906
47907     /**
47908      * Returns the grid's ColumnModel.
47909      * @return {ColumnModel}
47910      */
47911     getColumnModel : function(){
47912         return this.colModel;
47913     },
47914
47915     /**
47916      * Returns the grid's GridView object.
47917      * @return {GridView}
47918      */
47919     getView : function(){
47920         if(!this.view){
47921             this.view = new Roo.grid.GridView(this.viewConfig);
47922         }
47923         return this.view;
47924     },
47925     /**
47926      * Called to get grid's drag proxy text, by default returns this.ddText.
47927      * @return {String}
47928      */
47929     getDragDropText : function(){
47930         var count = this.selModel.getCount();
47931         return String.format(this.ddText, count, count == 1 ? '' : 's');
47932     }
47933 });
47934 /**
47935  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47936  * %0 is replaced with the number of selected rows.
47937  * @type String
47938  */
47939 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47940  * Based on:
47941  * Ext JS Library 1.1.1
47942  * Copyright(c) 2006-2007, Ext JS, LLC.
47943  *
47944  * Originally Released Under LGPL - original licence link has changed is not relivant.
47945  *
47946  * Fork - LGPL
47947  * <script type="text/javascript">
47948  */
47949  
47950 Roo.grid.AbstractGridView = function(){
47951         this.grid = null;
47952         
47953         this.events = {
47954             "beforerowremoved" : true,
47955             "beforerowsinserted" : true,
47956             "beforerefresh" : true,
47957             "rowremoved" : true,
47958             "rowsinserted" : true,
47959             "rowupdated" : true,
47960             "refresh" : true
47961         };
47962     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47963 };
47964
47965 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47966     rowClass : "x-grid-row",
47967     cellClass : "x-grid-cell",
47968     tdClass : "x-grid-td",
47969     hdClass : "x-grid-hd",
47970     splitClass : "x-grid-hd-split",
47971     
47972         init: function(grid){
47973         this.grid = grid;
47974                 var cid = this.grid.getGridEl().id;
47975         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47976         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47977         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47978         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47979         },
47980         
47981         getColumnRenderers : function(){
47982         var renderers = [];
47983         var cm = this.grid.colModel;
47984         var colCount = cm.getColumnCount();
47985         for(var i = 0; i < colCount; i++){
47986             renderers[i] = cm.getRenderer(i);
47987         }
47988         return renderers;
47989     },
47990     
47991     getColumnIds : function(){
47992         var ids = [];
47993         var cm = this.grid.colModel;
47994         var colCount = cm.getColumnCount();
47995         for(var i = 0; i < colCount; i++){
47996             ids[i] = cm.getColumnId(i);
47997         }
47998         return ids;
47999     },
48000     
48001     getDataIndexes : function(){
48002         if(!this.indexMap){
48003             this.indexMap = this.buildIndexMap();
48004         }
48005         return this.indexMap.colToData;
48006     },
48007     
48008     getColumnIndexByDataIndex : function(dataIndex){
48009         if(!this.indexMap){
48010             this.indexMap = this.buildIndexMap();
48011         }
48012         return this.indexMap.dataToCol[dataIndex];
48013     },
48014     
48015     /**
48016      * Set a css style for a column dynamically. 
48017      * @param {Number} colIndex The index of the column
48018      * @param {String} name The css property name
48019      * @param {String} value The css value
48020      */
48021     setCSSStyle : function(colIndex, name, value){
48022         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48023         Roo.util.CSS.updateRule(selector, name, value);
48024     },
48025     
48026     generateRules : function(cm){
48027         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48028         Roo.util.CSS.removeStyleSheet(rulesId);
48029         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48030             var cid = cm.getColumnId(i);
48031             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48032                          this.tdSelector, cid, " {\n}\n",
48033                          this.hdSelector, cid, " {\n}\n",
48034                          this.splitSelector, cid, " {\n}\n");
48035         }
48036         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48037     }
48038 });/*
48039  * Based on:
48040  * Ext JS Library 1.1.1
48041  * Copyright(c) 2006-2007, Ext JS, LLC.
48042  *
48043  * Originally Released Under LGPL - original licence link has changed is not relivant.
48044  *
48045  * Fork - LGPL
48046  * <script type="text/javascript">
48047  */
48048
48049 // private
48050 // This is a support class used internally by the Grid components
48051 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48052     this.grid = grid;
48053     this.view = grid.getView();
48054     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48055     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48056     if(hd2){
48057         this.setHandleElId(Roo.id(hd));
48058         this.setOuterHandleElId(Roo.id(hd2));
48059     }
48060     this.scroll = false;
48061 };
48062 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48063     maxDragWidth: 120,
48064     getDragData : function(e){
48065         var t = Roo.lib.Event.getTarget(e);
48066         var h = this.view.findHeaderCell(t);
48067         if(h){
48068             return {ddel: h.firstChild, header:h};
48069         }
48070         return false;
48071     },
48072
48073     onInitDrag : function(e){
48074         this.view.headersDisabled = true;
48075         var clone = this.dragData.ddel.cloneNode(true);
48076         clone.id = Roo.id();
48077         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48078         this.proxy.update(clone);
48079         return true;
48080     },
48081
48082     afterValidDrop : function(){
48083         var v = this.view;
48084         setTimeout(function(){
48085             v.headersDisabled = false;
48086         }, 50);
48087     },
48088
48089     afterInvalidDrop : function(){
48090         var v = this.view;
48091         setTimeout(function(){
48092             v.headersDisabled = false;
48093         }, 50);
48094     }
48095 });
48096 /*
48097  * Based on:
48098  * Ext JS Library 1.1.1
48099  * Copyright(c) 2006-2007, Ext JS, LLC.
48100  *
48101  * Originally Released Under LGPL - original licence link has changed is not relivant.
48102  *
48103  * Fork - LGPL
48104  * <script type="text/javascript">
48105  */
48106 // private
48107 // This is a support class used internally by the Grid components
48108 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48109     this.grid = grid;
48110     this.view = grid.getView();
48111     // split the proxies so they don't interfere with mouse events
48112     this.proxyTop = Roo.DomHelper.append(document.body, {
48113         cls:"col-move-top", html:"&#160;"
48114     }, true);
48115     this.proxyBottom = Roo.DomHelper.append(document.body, {
48116         cls:"col-move-bottom", html:"&#160;"
48117     }, true);
48118     this.proxyTop.hide = this.proxyBottom.hide = function(){
48119         this.setLeftTop(-100,-100);
48120         this.setStyle("visibility", "hidden");
48121     };
48122     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48123     // temporarily disabled
48124     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48125     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48126 };
48127 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48128     proxyOffsets : [-4, -9],
48129     fly: Roo.Element.fly,
48130
48131     getTargetFromEvent : function(e){
48132         var t = Roo.lib.Event.getTarget(e);
48133         var cindex = this.view.findCellIndex(t);
48134         if(cindex !== false){
48135             return this.view.getHeaderCell(cindex);
48136         }
48137         return null;
48138     },
48139
48140     nextVisible : function(h){
48141         var v = this.view, cm = this.grid.colModel;
48142         h = h.nextSibling;
48143         while(h){
48144             if(!cm.isHidden(v.getCellIndex(h))){
48145                 return h;
48146             }
48147             h = h.nextSibling;
48148         }
48149         return null;
48150     },
48151
48152     prevVisible : function(h){
48153         var v = this.view, cm = this.grid.colModel;
48154         h = h.prevSibling;
48155         while(h){
48156             if(!cm.isHidden(v.getCellIndex(h))){
48157                 return h;
48158             }
48159             h = h.prevSibling;
48160         }
48161         return null;
48162     },
48163
48164     positionIndicator : function(h, n, e){
48165         var x = Roo.lib.Event.getPageX(e);
48166         var r = Roo.lib.Dom.getRegion(n.firstChild);
48167         var px, pt, py = r.top + this.proxyOffsets[1];
48168         if((r.right - x) <= (r.right-r.left)/2){
48169             px = r.right+this.view.borderWidth;
48170             pt = "after";
48171         }else{
48172             px = r.left;
48173             pt = "before";
48174         }
48175         var oldIndex = this.view.getCellIndex(h);
48176         var newIndex = this.view.getCellIndex(n);
48177
48178         if(this.grid.colModel.isFixed(newIndex)){
48179             return false;
48180         }
48181
48182         var locked = this.grid.colModel.isLocked(newIndex);
48183
48184         if(pt == "after"){
48185             newIndex++;
48186         }
48187         if(oldIndex < newIndex){
48188             newIndex--;
48189         }
48190         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48191             return false;
48192         }
48193         px +=  this.proxyOffsets[0];
48194         this.proxyTop.setLeftTop(px, py);
48195         this.proxyTop.show();
48196         if(!this.bottomOffset){
48197             this.bottomOffset = this.view.mainHd.getHeight();
48198         }
48199         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48200         this.proxyBottom.show();
48201         return pt;
48202     },
48203
48204     onNodeEnter : function(n, dd, e, data){
48205         if(data.header != n){
48206             this.positionIndicator(data.header, n, e);
48207         }
48208     },
48209
48210     onNodeOver : function(n, dd, e, data){
48211         var result = false;
48212         if(data.header != n){
48213             result = this.positionIndicator(data.header, n, e);
48214         }
48215         if(!result){
48216             this.proxyTop.hide();
48217             this.proxyBottom.hide();
48218         }
48219         return result ? this.dropAllowed : this.dropNotAllowed;
48220     },
48221
48222     onNodeOut : function(n, dd, e, data){
48223         this.proxyTop.hide();
48224         this.proxyBottom.hide();
48225     },
48226
48227     onNodeDrop : function(n, dd, e, data){
48228         var h = data.header;
48229         if(h != n){
48230             var cm = this.grid.colModel;
48231             var x = Roo.lib.Event.getPageX(e);
48232             var r = Roo.lib.Dom.getRegion(n.firstChild);
48233             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48234             var oldIndex = this.view.getCellIndex(h);
48235             var newIndex = this.view.getCellIndex(n);
48236             var locked = cm.isLocked(newIndex);
48237             if(pt == "after"){
48238                 newIndex++;
48239             }
48240             if(oldIndex < newIndex){
48241                 newIndex--;
48242             }
48243             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48244                 return false;
48245             }
48246             cm.setLocked(oldIndex, locked, true);
48247             cm.moveColumn(oldIndex, newIndex);
48248             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48249             return true;
48250         }
48251         return false;
48252     }
48253 });
48254 /*
48255  * Based on:
48256  * Ext JS Library 1.1.1
48257  * Copyright(c) 2006-2007, Ext JS, LLC.
48258  *
48259  * Originally Released Under LGPL - original licence link has changed is not relivant.
48260  *
48261  * Fork - LGPL
48262  * <script type="text/javascript">
48263  */
48264   
48265 /**
48266  * @class Roo.grid.GridView
48267  * @extends Roo.util.Observable
48268  *
48269  * @constructor
48270  * @param {Object} config
48271  */
48272 Roo.grid.GridView = function(config){
48273     Roo.grid.GridView.superclass.constructor.call(this);
48274     this.el = null;
48275
48276     Roo.apply(this, config);
48277 };
48278
48279 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48280
48281     /**
48282      * Override this function to apply custom css classes to rows during rendering
48283      * @param {Record} record The record
48284      * @param {Number} index
48285      * @method getRowClass
48286      */
48287     rowClass : "x-grid-row",
48288
48289     cellClass : "x-grid-col",
48290
48291     tdClass : "x-grid-td",
48292
48293     hdClass : "x-grid-hd",
48294
48295     splitClass : "x-grid-split",
48296
48297     sortClasses : ["sort-asc", "sort-desc"],
48298
48299     enableMoveAnim : false,
48300
48301     hlColor: "C3DAF9",
48302
48303     dh : Roo.DomHelper,
48304
48305     fly : Roo.Element.fly,
48306
48307     css : Roo.util.CSS,
48308
48309     borderWidth: 1,
48310
48311     splitOffset: 3,
48312
48313     scrollIncrement : 22,
48314
48315     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48316
48317     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48318
48319     bind : function(ds, cm){
48320         if(this.ds){
48321             this.ds.un("load", this.onLoad, this);
48322             this.ds.un("datachanged", this.onDataChange, this);
48323             this.ds.un("add", this.onAdd, this);
48324             this.ds.un("remove", this.onRemove, this);
48325             this.ds.un("update", this.onUpdate, this);
48326             this.ds.un("clear", this.onClear, this);
48327         }
48328         if(ds){
48329             ds.on("load", this.onLoad, this);
48330             ds.on("datachanged", this.onDataChange, this);
48331             ds.on("add", this.onAdd, this);
48332             ds.on("remove", this.onRemove, this);
48333             ds.on("update", this.onUpdate, this);
48334             ds.on("clear", this.onClear, this);
48335         }
48336         this.ds = ds;
48337
48338         if(this.cm){
48339             this.cm.un("widthchange", this.onColWidthChange, this);
48340             this.cm.un("headerchange", this.onHeaderChange, this);
48341             this.cm.un("hiddenchange", this.onHiddenChange, this);
48342             this.cm.un("columnmoved", this.onColumnMove, this);
48343             this.cm.un("columnlockchange", this.onColumnLock, this);
48344         }
48345         if(cm){
48346             this.generateRules(cm);
48347             cm.on("widthchange", this.onColWidthChange, this);
48348             cm.on("headerchange", this.onHeaderChange, this);
48349             cm.on("hiddenchange", this.onHiddenChange, this);
48350             cm.on("columnmoved", this.onColumnMove, this);
48351             cm.on("columnlockchange", this.onColumnLock, this);
48352         }
48353         this.cm = cm;
48354     },
48355
48356     init: function(grid){
48357         Roo.grid.GridView.superclass.init.call(this, grid);
48358
48359         this.bind(grid.dataSource, grid.colModel);
48360
48361         grid.on("headerclick", this.handleHeaderClick, this);
48362
48363         if(grid.trackMouseOver){
48364             grid.on("mouseover", this.onRowOver, this);
48365             grid.on("mouseout", this.onRowOut, this);
48366         }
48367         grid.cancelTextSelection = function(){};
48368         this.gridId = grid.id;
48369
48370         var tpls = this.templates || {};
48371
48372         if(!tpls.master){
48373             tpls.master = new Roo.Template(
48374                '<div class="x-grid" hidefocus="true">',
48375                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48376                   '<div class="x-grid-topbar"></div>',
48377                   '<div class="x-grid-scroller"><div></div></div>',
48378                   '<div class="x-grid-locked">',
48379                       '<div class="x-grid-header">{lockedHeader}</div>',
48380                       '<div class="x-grid-body">{lockedBody}</div>',
48381                   "</div>",
48382                   '<div class="x-grid-viewport">',
48383                       '<div class="x-grid-header">{header}</div>',
48384                       '<div class="x-grid-body">{body}</div>',
48385                   "</div>",
48386                   '<div class="x-grid-bottombar"></div>',
48387                  
48388                   '<div class="x-grid-resize-proxy">&#160;</div>',
48389                "</div>"
48390             );
48391             tpls.master.disableformats = true;
48392         }
48393
48394         if(!tpls.header){
48395             tpls.header = new Roo.Template(
48396                '<table border="0" cellspacing="0" cellpadding="0">',
48397                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48398                "</table>{splits}"
48399             );
48400             tpls.header.disableformats = true;
48401         }
48402         tpls.header.compile();
48403
48404         if(!tpls.hcell){
48405             tpls.hcell = new Roo.Template(
48406                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48407                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48408                 "</div></td>"
48409              );
48410              tpls.hcell.disableFormats = true;
48411         }
48412         tpls.hcell.compile();
48413
48414         if(!tpls.hsplit){
48415             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48416             tpls.hsplit.disableFormats = true;
48417         }
48418         tpls.hsplit.compile();
48419
48420         if(!tpls.body){
48421             tpls.body = new Roo.Template(
48422                '<table border="0" cellspacing="0" cellpadding="0">',
48423                "<tbody>{rows}</tbody>",
48424                "</table>"
48425             );
48426             tpls.body.disableFormats = true;
48427         }
48428         tpls.body.compile();
48429
48430         if(!tpls.row){
48431             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48432             tpls.row.disableFormats = true;
48433         }
48434         tpls.row.compile();
48435
48436         if(!tpls.cell){
48437             tpls.cell = new Roo.Template(
48438                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48439                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48440                 "</td>"
48441             );
48442             tpls.cell.disableFormats = true;
48443         }
48444         tpls.cell.compile();
48445
48446         this.templates = tpls;
48447     },
48448
48449     // remap these for backwards compat
48450     onColWidthChange : function(){
48451         this.updateColumns.apply(this, arguments);
48452     },
48453     onHeaderChange : function(){
48454         this.updateHeaders.apply(this, arguments);
48455     }, 
48456     onHiddenChange : function(){
48457         this.handleHiddenChange.apply(this, arguments);
48458     },
48459     onColumnMove : function(){
48460         this.handleColumnMove.apply(this, arguments);
48461     },
48462     onColumnLock : function(){
48463         this.handleLockChange.apply(this, arguments);
48464     },
48465
48466     onDataChange : function(){
48467         this.refresh();
48468         this.updateHeaderSortState();
48469     },
48470
48471     onClear : function(){
48472         this.refresh();
48473     },
48474
48475     onUpdate : function(ds, record){
48476         this.refreshRow(record);
48477     },
48478
48479     refreshRow : function(record){
48480         var ds = this.ds, index;
48481         if(typeof record == 'number'){
48482             index = record;
48483             record = ds.getAt(index);
48484         }else{
48485             index = ds.indexOf(record);
48486         }
48487         this.insertRows(ds, index, index, true);
48488         this.onRemove(ds, record, index+1, true);
48489         this.syncRowHeights(index, index);
48490         this.layout();
48491         this.fireEvent("rowupdated", this, index, record);
48492     },
48493
48494     onAdd : function(ds, records, index){
48495         this.insertRows(ds, index, index + (records.length-1));
48496     },
48497
48498     onRemove : function(ds, record, index, isUpdate){
48499         if(isUpdate !== true){
48500             this.fireEvent("beforerowremoved", this, index, record);
48501         }
48502         var bt = this.getBodyTable(), lt = this.getLockedTable();
48503         if(bt.rows[index]){
48504             bt.firstChild.removeChild(bt.rows[index]);
48505         }
48506         if(lt.rows[index]){
48507             lt.firstChild.removeChild(lt.rows[index]);
48508         }
48509         if(isUpdate !== true){
48510             this.stripeRows(index);
48511             this.syncRowHeights(index, index);
48512             this.layout();
48513             this.fireEvent("rowremoved", this, index, record);
48514         }
48515     },
48516
48517     onLoad : function(){
48518         this.scrollToTop();
48519     },
48520
48521     /**
48522      * Scrolls the grid to the top
48523      */
48524     scrollToTop : function(){
48525         if(this.scroller){
48526             this.scroller.dom.scrollTop = 0;
48527             this.syncScroll();
48528         }
48529     },
48530
48531     /**
48532      * Gets a panel in the header of the grid that can be used for toolbars etc.
48533      * After modifying the contents of this panel a call to grid.autoSize() may be
48534      * required to register any changes in size.
48535      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48536      * @return Roo.Element
48537      */
48538     getHeaderPanel : function(doShow){
48539         if(doShow){
48540             this.headerPanel.show();
48541         }
48542         return this.headerPanel;
48543     },
48544
48545     /**
48546      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48547      * After modifying the contents of this panel a call to grid.autoSize() may be
48548      * required to register any changes in size.
48549      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48550      * @return Roo.Element
48551      */
48552     getFooterPanel : function(doShow){
48553         if(doShow){
48554             this.footerPanel.show();
48555         }
48556         return this.footerPanel;
48557     },
48558
48559     initElements : function(){
48560         var E = Roo.Element;
48561         var el = this.grid.getGridEl().dom.firstChild;
48562         var cs = el.childNodes;
48563
48564         this.el = new E(el);
48565         
48566          this.focusEl = new E(el.firstChild);
48567         this.focusEl.swallowEvent("click", true);
48568         
48569         this.headerPanel = new E(cs[1]);
48570         this.headerPanel.enableDisplayMode("block");
48571
48572         this.scroller = new E(cs[2]);
48573         this.scrollSizer = new E(this.scroller.dom.firstChild);
48574
48575         this.lockedWrap = new E(cs[3]);
48576         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48577         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48578
48579         this.mainWrap = new E(cs[4]);
48580         this.mainHd = new E(this.mainWrap.dom.firstChild);
48581         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48582
48583         this.footerPanel = new E(cs[5]);
48584         this.footerPanel.enableDisplayMode("block");
48585
48586         this.resizeProxy = new E(cs[6]);
48587
48588         this.headerSelector = String.format(
48589            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48590            this.lockedHd.id, this.mainHd.id
48591         );
48592
48593         this.splitterSelector = String.format(
48594            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48595            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48596         );
48597     },
48598     idToCssName : function(s)
48599     {
48600         return s.replace(/[^a-z0-9]+/ig, '-');
48601     },
48602
48603     getHeaderCell : function(index){
48604         return Roo.DomQuery.select(this.headerSelector)[index];
48605     },
48606
48607     getHeaderCellMeasure : function(index){
48608         return this.getHeaderCell(index).firstChild;
48609     },
48610
48611     getHeaderCellText : function(index){
48612         return this.getHeaderCell(index).firstChild.firstChild;
48613     },
48614
48615     getLockedTable : function(){
48616         return this.lockedBody.dom.firstChild;
48617     },
48618
48619     getBodyTable : function(){
48620         return this.mainBody.dom.firstChild;
48621     },
48622
48623     getLockedRow : function(index){
48624         return this.getLockedTable().rows[index];
48625     },
48626
48627     getRow : function(index){
48628         return this.getBodyTable().rows[index];
48629     },
48630
48631     getRowComposite : function(index){
48632         if(!this.rowEl){
48633             this.rowEl = new Roo.CompositeElementLite();
48634         }
48635         var els = [], lrow, mrow;
48636         if(lrow = this.getLockedRow(index)){
48637             els.push(lrow);
48638         }
48639         if(mrow = this.getRow(index)){
48640             els.push(mrow);
48641         }
48642         this.rowEl.elements = els;
48643         return this.rowEl;
48644     },
48645     /**
48646      * Gets the 'td' of the cell
48647      * 
48648      * @param {Integer} rowIndex row to select
48649      * @param {Integer} colIndex column to select
48650      * 
48651      * @return {Object} 
48652      */
48653     getCell : function(rowIndex, colIndex){
48654         var locked = this.cm.getLockedCount();
48655         var source;
48656         if(colIndex < locked){
48657             source = this.lockedBody.dom.firstChild;
48658         }else{
48659             source = this.mainBody.dom.firstChild;
48660             colIndex -= locked;
48661         }
48662         return source.rows[rowIndex].childNodes[colIndex];
48663     },
48664
48665     getCellText : function(rowIndex, colIndex){
48666         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48667     },
48668
48669     getCellBox : function(cell){
48670         var b = this.fly(cell).getBox();
48671         if(Roo.isOpera){ // opera fails to report the Y
48672             b.y = cell.offsetTop + this.mainBody.getY();
48673         }
48674         return b;
48675     },
48676
48677     getCellIndex : function(cell){
48678         var id = String(cell.className).match(this.cellRE);
48679         if(id){
48680             return parseInt(id[1], 10);
48681         }
48682         return 0;
48683     },
48684
48685     findHeaderIndex : function(n){
48686         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48687         return r ? this.getCellIndex(r) : false;
48688     },
48689
48690     findHeaderCell : function(n){
48691         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48692         return r ? r : false;
48693     },
48694
48695     findRowIndex : function(n){
48696         if(!n){
48697             return false;
48698         }
48699         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48700         return r ? r.rowIndex : false;
48701     },
48702
48703     findCellIndex : function(node){
48704         var stop = this.el.dom;
48705         while(node && node != stop){
48706             if(this.findRE.test(node.className)){
48707                 return this.getCellIndex(node);
48708             }
48709             node = node.parentNode;
48710         }
48711         return false;
48712     },
48713
48714     getColumnId : function(index){
48715         return this.cm.getColumnId(index);
48716     },
48717
48718     getSplitters : function()
48719     {
48720         if(this.splitterSelector){
48721            return Roo.DomQuery.select(this.splitterSelector);
48722         }else{
48723             return null;
48724       }
48725     },
48726
48727     getSplitter : function(index){
48728         return this.getSplitters()[index];
48729     },
48730
48731     onRowOver : function(e, t){
48732         var row;
48733         if((row = this.findRowIndex(t)) !== false){
48734             this.getRowComposite(row).addClass("x-grid-row-over");
48735         }
48736     },
48737
48738     onRowOut : function(e, t){
48739         var row;
48740         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48741             this.getRowComposite(row).removeClass("x-grid-row-over");
48742         }
48743     },
48744
48745     renderHeaders : function(){
48746         var cm = this.cm;
48747         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48748         var cb = [], lb = [], sb = [], lsb = [], p = {};
48749         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48750             p.cellId = "x-grid-hd-0-" + i;
48751             p.splitId = "x-grid-csplit-0-" + i;
48752             p.id = cm.getColumnId(i);
48753             p.title = cm.getColumnTooltip(i) || "";
48754             p.value = cm.getColumnHeader(i) || "";
48755             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48756             if(!cm.isLocked(i)){
48757                 cb[cb.length] = ct.apply(p);
48758                 sb[sb.length] = st.apply(p);
48759             }else{
48760                 lb[lb.length] = ct.apply(p);
48761                 lsb[lsb.length] = st.apply(p);
48762             }
48763         }
48764         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48765                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48766     },
48767
48768     updateHeaders : function(){
48769         var html = this.renderHeaders();
48770         this.lockedHd.update(html[0]);
48771         this.mainHd.update(html[1]);
48772     },
48773
48774     /**
48775      * Focuses the specified row.
48776      * @param {Number} row The row index
48777      */
48778     focusRow : function(row)
48779     {
48780         //Roo.log('GridView.focusRow');
48781         var x = this.scroller.dom.scrollLeft;
48782         this.focusCell(row, 0, false);
48783         this.scroller.dom.scrollLeft = x;
48784     },
48785
48786     /**
48787      * Focuses the specified cell.
48788      * @param {Number} row The row index
48789      * @param {Number} col The column index
48790      * @param {Boolean} hscroll false to disable horizontal scrolling
48791      */
48792     focusCell : function(row, col, hscroll)
48793     {
48794         //Roo.log('GridView.focusCell');
48795         var el = this.ensureVisible(row, col, hscroll);
48796         this.focusEl.alignTo(el, "tl-tl");
48797         if(Roo.isGecko){
48798             this.focusEl.focus();
48799         }else{
48800             this.focusEl.focus.defer(1, this.focusEl);
48801         }
48802     },
48803
48804     /**
48805      * Scrolls the specified cell into view
48806      * @param {Number} row The row index
48807      * @param {Number} col The column index
48808      * @param {Boolean} hscroll false to disable horizontal scrolling
48809      */
48810     ensureVisible : function(row, col, hscroll)
48811     {
48812         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48813         //return null; //disable for testing.
48814         if(typeof row != "number"){
48815             row = row.rowIndex;
48816         }
48817         if(row < 0 && row >= this.ds.getCount()){
48818             return  null;
48819         }
48820         col = (col !== undefined ? col : 0);
48821         var cm = this.grid.colModel;
48822         while(cm.isHidden(col)){
48823             col++;
48824         }
48825
48826         var el = this.getCell(row, col);
48827         if(!el){
48828             return null;
48829         }
48830         var c = this.scroller.dom;
48831
48832         var ctop = parseInt(el.offsetTop, 10);
48833         var cleft = parseInt(el.offsetLeft, 10);
48834         var cbot = ctop + el.offsetHeight;
48835         var cright = cleft + el.offsetWidth;
48836         
48837         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48838         var stop = parseInt(c.scrollTop, 10);
48839         var sleft = parseInt(c.scrollLeft, 10);
48840         var sbot = stop + ch;
48841         var sright = sleft + c.clientWidth;
48842         /*
48843         Roo.log('GridView.ensureVisible:' +
48844                 ' ctop:' + ctop +
48845                 ' c.clientHeight:' + c.clientHeight +
48846                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48847                 ' stop:' + stop +
48848                 ' cbot:' + cbot +
48849                 ' sbot:' + sbot +
48850                 ' ch:' + ch  
48851                 );
48852         */
48853         if(ctop < stop){
48854              c.scrollTop = ctop;
48855             //Roo.log("set scrolltop to ctop DISABLE?");
48856         }else if(cbot > sbot){
48857             //Roo.log("set scrolltop to cbot-ch");
48858             c.scrollTop = cbot-ch;
48859         }
48860         
48861         if(hscroll !== false){
48862             if(cleft < sleft){
48863                 c.scrollLeft = cleft;
48864             }else if(cright > sright){
48865                 c.scrollLeft = cright-c.clientWidth;
48866             }
48867         }
48868          
48869         return el;
48870     },
48871
48872     updateColumns : function(){
48873         this.grid.stopEditing();
48874         var cm = this.grid.colModel, colIds = this.getColumnIds();
48875         //var totalWidth = cm.getTotalWidth();
48876         var pos = 0;
48877         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48878             //if(cm.isHidden(i)) continue;
48879             var w = cm.getColumnWidth(i);
48880             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48881             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48882         }
48883         this.updateSplitters();
48884     },
48885
48886     generateRules : function(cm){
48887         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48888         Roo.util.CSS.removeStyleSheet(rulesId);
48889         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48890             var cid = cm.getColumnId(i);
48891             var align = '';
48892             if(cm.config[i].align){
48893                 align = 'text-align:'+cm.config[i].align+';';
48894             }
48895             var hidden = '';
48896             if(cm.isHidden(i)){
48897                 hidden = 'display:none;';
48898             }
48899             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48900             ruleBuf.push(
48901                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48902                     this.hdSelector, cid, " {\n", align, width, "}\n",
48903                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48904                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48905         }
48906         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48907     },
48908
48909     updateSplitters : function(){
48910         var cm = this.cm, s = this.getSplitters();
48911         if(s){ // splitters not created yet
48912             var pos = 0, locked = true;
48913             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48914                 if(cm.isHidden(i)) continue;
48915                 var w = cm.getColumnWidth(i); // make sure it's a number
48916                 if(!cm.isLocked(i) && locked){
48917                     pos = 0;
48918                     locked = false;
48919                 }
48920                 pos += w;
48921                 s[i].style.left = (pos-this.splitOffset) + "px";
48922             }
48923         }
48924     },
48925
48926     handleHiddenChange : function(colModel, colIndex, hidden){
48927         if(hidden){
48928             this.hideColumn(colIndex);
48929         }else{
48930             this.unhideColumn(colIndex);
48931         }
48932     },
48933
48934     hideColumn : function(colIndex){
48935         var cid = this.getColumnId(colIndex);
48936         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48937         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48938         if(Roo.isSafari){
48939             this.updateHeaders();
48940         }
48941         this.updateSplitters();
48942         this.layout();
48943     },
48944
48945     unhideColumn : function(colIndex){
48946         var cid = this.getColumnId(colIndex);
48947         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48948         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48949
48950         if(Roo.isSafari){
48951             this.updateHeaders();
48952         }
48953         this.updateSplitters();
48954         this.layout();
48955     },
48956
48957     insertRows : function(dm, firstRow, lastRow, isUpdate){
48958         if(firstRow == 0 && lastRow == dm.getCount()-1){
48959             this.refresh();
48960         }else{
48961             if(!isUpdate){
48962                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48963             }
48964             var s = this.getScrollState();
48965             var markup = this.renderRows(firstRow, lastRow);
48966             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48967             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48968             this.restoreScroll(s);
48969             if(!isUpdate){
48970                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48971                 this.syncRowHeights(firstRow, lastRow);
48972                 this.stripeRows(firstRow);
48973                 this.layout();
48974             }
48975         }
48976     },
48977
48978     bufferRows : function(markup, target, index){
48979         var before = null, trows = target.rows, tbody = target.tBodies[0];
48980         if(index < trows.length){
48981             before = trows[index];
48982         }
48983         var b = document.createElement("div");
48984         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48985         var rows = b.firstChild.rows;
48986         for(var i = 0, len = rows.length; i < len; i++){
48987             if(before){
48988                 tbody.insertBefore(rows[0], before);
48989             }else{
48990                 tbody.appendChild(rows[0]);
48991             }
48992         }
48993         b.innerHTML = "";
48994         b = null;
48995     },
48996
48997     deleteRows : function(dm, firstRow, lastRow){
48998         if(dm.getRowCount()<1){
48999             this.fireEvent("beforerefresh", this);
49000             this.mainBody.update("");
49001             this.lockedBody.update("");
49002             this.fireEvent("refresh", this);
49003         }else{
49004             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49005             var bt = this.getBodyTable();
49006             var tbody = bt.firstChild;
49007             var rows = bt.rows;
49008             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49009                 tbody.removeChild(rows[firstRow]);
49010             }
49011             this.stripeRows(firstRow);
49012             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49013         }
49014     },
49015
49016     updateRows : function(dataSource, firstRow, lastRow){
49017         var s = this.getScrollState();
49018         this.refresh();
49019         this.restoreScroll(s);
49020     },
49021
49022     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49023         if(!noRefresh){
49024            this.refresh();
49025         }
49026         this.updateHeaderSortState();
49027     },
49028
49029     getScrollState : function(){
49030         
49031         var sb = this.scroller.dom;
49032         return {left: sb.scrollLeft, top: sb.scrollTop};
49033     },
49034
49035     stripeRows : function(startRow){
49036         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49037             return;
49038         }
49039         startRow = startRow || 0;
49040         var rows = this.getBodyTable().rows;
49041         var lrows = this.getLockedTable().rows;
49042         var cls = ' x-grid-row-alt ';
49043         for(var i = startRow, len = rows.length; i < len; i++){
49044             var row = rows[i], lrow = lrows[i];
49045             var isAlt = ((i+1) % 2 == 0);
49046             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49047             if(isAlt == hasAlt){
49048                 continue;
49049             }
49050             if(isAlt){
49051                 row.className += " x-grid-row-alt";
49052             }else{
49053                 row.className = row.className.replace("x-grid-row-alt", "");
49054             }
49055             if(lrow){
49056                 lrow.className = row.className;
49057             }
49058         }
49059     },
49060
49061     restoreScroll : function(state){
49062         //Roo.log('GridView.restoreScroll');
49063         var sb = this.scroller.dom;
49064         sb.scrollLeft = state.left;
49065         sb.scrollTop = state.top;
49066         this.syncScroll();
49067     },
49068
49069     syncScroll : function(){
49070         //Roo.log('GridView.syncScroll');
49071         var sb = this.scroller.dom;
49072         var sh = this.mainHd.dom;
49073         var bs = this.mainBody.dom;
49074         var lv = this.lockedBody.dom;
49075         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49076         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49077     },
49078
49079     handleScroll : function(e){
49080         this.syncScroll();
49081         var sb = this.scroller.dom;
49082         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49083         e.stopEvent();
49084     },
49085
49086     handleWheel : function(e){
49087         var d = e.getWheelDelta();
49088         this.scroller.dom.scrollTop -= d*22;
49089         // set this here to prevent jumpy scrolling on large tables
49090         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49091         e.stopEvent();
49092     },
49093
49094     renderRows : function(startRow, endRow){
49095         // pull in all the crap needed to render rows
49096         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49097         var colCount = cm.getColumnCount();
49098
49099         if(ds.getCount() < 1){
49100             return ["", ""];
49101         }
49102
49103         // build a map for all the columns
49104         var cs = [];
49105         for(var i = 0; i < colCount; i++){
49106             var name = cm.getDataIndex(i);
49107             cs[i] = {
49108                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49109                 renderer : cm.getRenderer(i),
49110                 id : cm.getColumnId(i),
49111                 locked : cm.isLocked(i)
49112             };
49113         }
49114
49115         startRow = startRow || 0;
49116         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49117
49118         // records to render
49119         var rs = ds.getRange(startRow, endRow);
49120
49121         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49122     },
49123
49124     // As much as I hate to duplicate code, this was branched because FireFox really hates
49125     // [].join("") on strings. The performance difference was substantial enough to
49126     // branch this function
49127     doRender : Roo.isGecko ?
49128             function(cs, rs, ds, startRow, colCount, stripe){
49129                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49130                 // buffers
49131                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49132                 
49133                 var hasListener = this.grid.hasListener('rowclass');
49134                 var rowcfg = {};
49135                 for(var j = 0, len = rs.length; j < len; j++){
49136                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49137                     for(var i = 0; i < colCount; i++){
49138                         c = cs[i];
49139                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49140                         p.id = c.id;
49141                         p.css = p.attr = "";
49142                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49143                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49144                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49145                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49146                         }
49147                         var markup = ct.apply(p);
49148                         if(!c.locked){
49149                             cb+= markup;
49150                         }else{
49151                             lcb+= markup;
49152                         }
49153                     }
49154                     var alt = [];
49155                     if(stripe && ((rowIndex+1) % 2 == 0)){
49156                         alt.push("x-grid-row-alt")
49157                     }
49158                     if(r.dirty){
49159                         alt.push(  " x-grid-dirty-row");
49160                     }
49161                     rp.cells = lcb;
49162                     if(this.getRowClass){
49163                         alt.push(this.getRowClass(r, rowIndex));
49164                     }
49165                     if (hasListener) {
49166                         rowcfg = {
49167                              
49168                             record: r,
49169                             rowIndex : rowIndex,
49170                             rowClass : ''
49171                         }
49172                         this.grid.fireEvent('rowclass', this, rowcfg);
49173                         alt.push(rowcfg.rowClass);
49174                     }
49175                     rp.alt = alt.join(" ");
49176                     lbuf+= rt.apply(rp);
49177                     rp.cells = cb;
49178                     buf+=  rt.apply(rp);
49179                 }
49180                 return [lbuf, buf];
49181             } :
49182             function(cs, rs, ds, startRow, colCount, stripe){
49183                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49184                 // buffers
49185                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49186                 var hasListener = this.grid.hasListener('rowclass');
49187                 var rowcfg = {};
49188                 for(var j = 0, len = rs.length; j < len; j++){
49189                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49190                     for(var i = 0; i < colCount; i++){
49191                         c = cs[i];
49192                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49193                         p.id = c.id;
49194                         p.css = p.attr = "";
49195                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49196                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49197                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49198                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49199                         }
49200                         var markup = ct.apply(p);
49201                         if(!c.locked){
49202                             cb[cb.length] = markup;
49203                         }else{
49204                             lcb[lcb.length] = markup;
49205                         }
49206                     }
49207                     var alt = [];
49208                     if(stripe && ((rowIndex+1) % 2 == 0)){
49209                         alt.push( "x-grid-row-alt");
49210                     }
49211                     if(r.dirty){
49212                         alt.push(" x-grid-dirty-row");
49213                     }
49214                     rp.cells = lcb;
49215                     if(this.getRowClass){
49216                         alt.push( this.getRowClass(r, rowIndex));
49217                     }
49218                     if (hasListener) {
49219                         rowcfg = {
49220                              
49221                             record: r,
49222                             rowIndex : rowIndex,
49223                             rowClass : ''
49224                         }
49225                         this.grid.fireEvent('rowclass', this, rowcfg);
49226                         alt.push(rowcfg.rowClass);
49227                     }
49228                     rp.alt = alt.join(" ");
49229                     rp.cells = lcb.join("");
49230                     lbuf[lbuf.length] = rt.apply(rp);
49231                     rp.cells = cb.join("");
49232                     buf[buf.length] =  rt.apply(rp);
49233                 }
49234                 return [lbuf.join(""), buf.join("")];
49235             },
49236
49237     renderBody : function(){
49238         var markup = this.renderRows();
49239         var bt = this.templates.body;
49240         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49241     },
49242
49243     /**
49244      * Refreshes the grid
49245      * @param {Boolean} headersToo
49246      */
49247     refresh : function(headersToo){
49248         this.fireEvent("beforerefresh", this);
49249         this.grid.stopEditing();
49250         var result = this.renderBody();
49251         this.lockedBody.update(result[0]);
49252         this.mainBody.update(result[1]);
49253         if(headersToo === true){
49254             this.updateHeaders();
49255             this.updateColumns();
49256             this.updateSplitters();
49257             this.updateHeaderSortState();
49258         }
49259         this.syncRowHeights();
49260         this.layout();
49261         this.fireEvent("refresh", this);
49262     },
49263
49264     handleColumnMove : function(cm, oldIndex, newIndex){
49265         this.indexMap = null;
49266         var s = this.getScrollState();
49267         this.refresh(true);
49268         this.restoreScroll(s);
49269         this.afterMove(newIndex);
49270     },
49271
49272     afterMove : function(colIndex){
49273         if(this.enableMoveAnim && Roo.enableFx){
49274             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49275         }
49276         // if multisort - fix sortOrder, and reload..
49277         if (this.grid.dataSource.multiSort) {
49278             // the we can call sort again..
49279             var dm = this.grid.dataSource;
49280             var cm = this.grid.colModel;
49281             var so = [];
49282             for(var i = 0; i < cm.config.length; i++ ) {
49283                 
49284                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49285                     continue; // dont' bother, it's not in sort list or being set.
49286                 }
49287                 
49288                 so.push(cm.config[i].dataIndex);
49289             };
49290             dm.sortOrder = so;
49291             dm.load(dm.lastOptions);
49292             
49293             
49294         }
49295         
49296     },
49297
49298     updateCell : function(dm, rowIndex, dataIndex){
49299         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49300         if(typeof colIndex == "undefined"){ // not present in grid
49301             return;
49302         }
49303         var cm = this.grid.colModel;
49304         var cell = this.getCell(rowIndex, colIndex);
49305         var cellText = this.getCellText(rowIndex, colIndex);
49306
49307         var p = {
49308             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49309             id : cm.getColumnId(colIndex),
49310             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49311         };
49312         var renderer = cm.getRenderer(colIndex);
49313         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49314         if(typeof val == "undefined" || val === "") val = "&#160;";
49315         cellText.innerHTML = val;
49316         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49317         this.syncRowHeights(rowIndex, rowIndex);
49318     },
49319
49320     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49321         var maxWidth = 0;
49322         if(this.grid.autoSizeHeaders){
49323             var h = this.getHeaderCellMeasure(colIndex);
49324             maxWidth = Math.max(maxWidth, h.scrollWidth);
49325         }
49326         var tb, index;
49327         if(this.cm.isLocked(colIndex)){
49328             tb = this.getLockedTable();
49329             index = colIndex;
49330         }else{
49331             tb = this.getBodyTable();
49332             index = colIndex - this.cm.getLockedCount();
49333         }
49334         if(tb && tb.rows){
49335             var rows = tb.rows;
49336             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49337             for(var i = 0; i < stopIndex; i++){
49338                 var cell = rows[i].childNodes[index].firstChild;
49339                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49340             }
49341         }
49342         return maxWidth + /*margin for error in IE*/ 5;
49343     },
49344     /**
49345      * Autofit a column to its content.
49346      * @param {Number} colIndex
49347      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49348      */
49349      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49350          if(this.cm.isHidden(colIndex)){
49351              return; // can't calc a hidden column
49352          }
49353         if(forceMinSize){
49354             var cid = this.cm.getColumnId(colIndex);
49355             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49356            if(this.grid.autoSizeHeaders){
49357                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49358            }
49359         }
49360         var newWidth = this.calcColumnWidth(colIndex);
49361         this.cm.setColumnWidth(colIndex,
49362             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49363         if(!suppressEvent){
49364             this.grid.fireEvent("columnresize", colIndex, newWidth);
49365         }
49366     },
49367
49368     /**
49369      * Autofits all columns to their content and then expands to fit any extra space in the grid
49370      */
49371      autoSizeColumns : function(){
49372         var cm = this.grid.colModel;
49373         var colCount = cm.getColumnCount();
49374         for(var i = 0; i < colCount; i++){
49375             this.autoSizeColumn(i, true, true);
49376         }
49377         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49378             this.fitColumns();
49379         }else{
49380             this.updateColumns();
49381             this.layout();
49382         }
49383     },
49384
49385     /**
49386      * Autofits all columns to the grid's width proportionate with their current size
49387      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49388      */
49389     fitColumns : function(reserveScrollSpace){
49390         var cm = this.grid.colModel;
49391         var colCount = cm.getColumnCount();
49392         var cols = [];
49393         var width = 0;
49394         var i, w;
49395         for (i = 0; i < colCount; i++){
49396             if(!cm.isHidden(i) && !cm.isFixed(i)){
49397                 w = cm.getColumnWidth(i);
49398                 cols.push(i);
49399                 cols.push(w);
49400                 width += w;
49401             }
49402         }
49403         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49404         if(reserveScrollSpace){
49405             avail -= 17;
49406         }
49407         var frac = (avail - cm.getTotalWidth())/width;
49408         while (cols.length){
49409             w = cols.pop();
49410             i = cols.pop();
49411             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49412         }
49413         this.updateColumns();
49414         this.layout();
49415     },
49416
49417     onRowSelect : function(rowIndex){
49418         var row = this.getRowComposite(rowIndex);
49419         row.addClass("x-grid-row-selected");
49420     },
49421
49422     onRowDeselect : function(rowIndex){
49423         var row = this.getRowComposite(rowIndex);
49424         row.removeClass("x-grid-row-selected");
49425     },
49426
49427     onCellSelect : function(row, col){
49428         var cell = this.getCell(row, col);
49429         if(cell){
49430             Roo.fly(cell).addClass("x-grid-cell-selected");
49431         }
49432     },
49433
49434     onCellDeselect : function(row, col){
49435         var cell = this.getCell(row, col);
49436         if(cell){
49437             Roo.fly(cell).removeClass("x-grid-cell-selected");
49438         }
49439     },
49440
49441     updateHeaderSortState : function(){
49442         
49443         // sort state can be single { field: xxx, direction : yyy}
49444         // or   { xxx=>ASC , yyy : DESC ..... }
49445         
49446         var mstate = {};
49447         if (!this.ds.multiSort) { 
49448             var state = this.ds.getSortState();
49449             if(!state){
49450                 return;
49451             }
49452             mstate[state.field] = state.direction;
49453             // FIXME... - this is not used here.. but might be elsewhere..
49454             this.sortState = state;
49455             
49456         } else {
49457             mstate = this.ds.sortToggle;
49458         }
49459         //remove existing sort classes..
49460         
49461         var sc = this.sortClasses;
49462         var hds = this.el.select(this.headerSelector).removeClass(sc);
49463         
49464         for(var f in mstate) {
49465         
49466             var sortColumn = this.cm.findColumnIndex(f);
49467             
49468             if(sortColumn != -1){
49469                 var sortDir = mstate[f];        
49470                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49471             }
49472         }
49473         
49474          
49475         
49476     },
49477
49478
49479     handleHeaderClick : function(g, index){
49480         if(this.headersDisabled){
49481             return;
49482         }
49483         var dm = g.dataSource, cm = g.colModel;
49484         if(!cm.isSortable(index)){
49485             return;
49486         }
49487         g.stopEditing();
49488         
49489         if (dm.multiSort) {
49490             // update the sortOrder
49491             var so = [];
49492             for(var i = 0; i < cm.config.length; i++ ) {
49493                 
49494                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49495                     continue; // dont' bother, it's not in sort list or being set.
49496                 }
49497                 
49498                 so.push(cm.config[i].dataIndex);
49499             };
49500             dm.sortOrder = so;
49501         }
49502         
49503         
49504         dm.sort(cm.getDataIndex(index));
49505     },
49506
49507
49508     destroy : function(){
49509         if(this.colMenu){
49510             this.colMenu.removeAll();
49511             Roo.menu.MenuMgr.unregister(this.colMenu);
49512             this.colMenu.getEl().remove();
49513             delete this.colMenu;
49514         }
49515         if(this.hmenu){
49516             this.hmenu.removeAll();
49517             Roo.menu.MenuMgr.unregister(this.hmenu);
49518             this.hmenu.getEl().remove();
49519             delete this.hmenu;
49520         }
49521         if(this.grid.enableColumnMove){
49522             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49523             if(dds){
49524                 for(var dd in dds){
49525                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49526                         var elid = dds[dd].dragElId;
49527                         dds[dd].unreg();
49528                         Roo.get(elid).remove();
49529                     } else if(dds[dd].config.isTarget){
49530                         dds[dd].proxyTop.remove();
49531                         dds[dd].proxyBottom.remove();
49532                         dds[dd].unreg();
49533                     }
49534                     if(Roo.dd.DDM.locationCache[dd]){
49535                         delete Roo.dd.DDM.locationCache[dd];
49536                     }
49537                 }
49538                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49539             }
49540         }
49541         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49542         this.bind(null, null);
49543         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49544     },
49545
49546     handleLockChange : function(){
49547         this.refresh(true);
49548     },
49549
49550     onDenyColumnLock : function(){
49551
49552     },
49553
49554     onDenyColumnHide : function(){
49555
49556     },
49557
49558     handleHdMenuClick : function(item){
49559         var index = this.hdCtxIndex;
49560         var cm = this.cm, ds = this.ds;
49561         switch(item.id){
49562             case "asc":
49563                 ds.sort(cm.getDataIndex(index), "ASC");
49564                 break;
49565             case "desc":
49566                 ds.sort(cm.getDataIndex(index), "DESC");
49567                 break;
49568             case "lock":
49569                 var lc = cm.getLockedCount();
49570                 if(cm.getColumnCount(true) <= lc+1){
49571                     this.onDenyColumnLock();
49572                     return;
49573                 }
49574                 if(lc != index){
49575                     cm.setLocked(index, true, true);
49576                     cm.moveColumn(index, lc);
49577                     this.grid.fireEvent("columnmove", index, lc);
49578                 }else{
49579                     cm.setLocked(index, true);
49580                 }
49581             break;
49582             case "unlock":
49583                 var lc = cm.getLockedCount();
49584                 if((lc-1) != index){
49585                     cm.setLocked(index, false, true);
49586                     cm.moveColumn(index, lc-1);
49587                     this.grid.fireEvent("columnmove", index, lc-1);
49588                 }else{
49589                     cm.setLocked(index, false);
49590                 }
49591             break;
49592             default:
49593                 index = cm.getIndexById(item.id.substr(4));
49594                 if(index != -1){
49595                     if(item.checked && cm.getColumnCount(true) <= 1){
49596                         this.onDenyColumnHide();
49597                         return false;
49598                     }
49599                     cm.setHidden(index, item.checked);
49600                 }
49601         }
49602         return true;
49603     },
49604
49605     beforeColMenuShow : function(){
49606         var cm = this.cm,  colCount = cm.getColumnCount();
49607         this.colMenu.removeAll();
49608         for(var i = 0; i < colCount; i++){
49609             this.colMenu.add(new Roo.menu.CheckItem({
49610                 id: "col-"+cm.getColumnId(i),
49611                 text: cm.getColumnHeader(i),
49612                 checked: !cm.isHidden(i),
49613                 hideOnClick:false
49614             }));
49615         }
49616     },
49617
49618     handleHdCtx : function(g, index, e){
49619         e.stopEvent();
49620         var hd = this.getHeaderCell(index);
49621         this.hdCtxIndex = index;
49622         var ms = this.hmenu.items, cm = this.cm;
49623         ms.get("asc").setDisabled(!cm.isSortable(index));
49624         ms.get("desc").setDisabled(!cm.isSortable(index));
49625         if(this.grid.enableColLock !== false){
49626             ms.get("lock").setDisabled(cm.isLocked(index));
49627             ms.get("unlock").setDisabled(!cm.isLocked(index));
49628         }
49629         this.hmenu.show(hd, "tl-bl");
49630     },
49631
49632     handleHdOver : function(e){
49633         var hd = this.findHeaderCell(e.getTarget());
49634         if(hd && !this.headersDisabled){
49635             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49636                this.fly(hd).addClass("x-grid-hd-over");
49637             }
49638         }
49639     },
49640
49641     handleHdOut : function(e){
49642         var hd = this.findHeaderCell(e.getTarget());
49643         if(hd){
49644             this.fly(hd).removeClass("x-grid-hd-over");
49645         }
49646     },
49647
49648     handleSplitDblClick : function(e, t){
49649         var i = this.getCellIndex(t);
49650         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49651             this.autoSizeColumn(i, true);
49652             this.layout();
49653         }
49654     },
49655
49656     render : function(){
49657
49658         var cm = this.cm;
49659         var colCount = cm.getColumnCount();
49660
49661         if(this.grid.monitorWindowResize === true){
49662             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49663         }
49664         var header = this.renderHeaders();
49665         var body = this.templates.body.apply({rows:""});
49666         var html = this.templates.master.apply({
49667             lockedBody: body,
49668             body: body,
49669             lockedHeader: header[0],
49670             header: header[1]
49671         });
49672
49673         //this.updateColumns();
49674
49675         this.grid.getGridEl().dom.innerHTML = html;
49676
49677         this.initElements();
49678         
49679         // a kludge to fix the random scolling effect in webkit
49680         this.el.on("scroll", function() {
49681             this.el.dom.scrollTop=0; // hopefully not recursive..
49682         },this);
49683
49684         this.scroller.on("scroll", this.handleScroll, this);
49685         this.lockedBody.on("mousewheel", this.handleWheel, this);
49686         this.mainBody.on("mousewheel", this.handleWheel, this);
49687
49688         this.mainHd.on("mouseover", this.handleHdOver, this);
49689         this.mainHd.on("mouseout", this.handleHdOut, this);
49690         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49691                 {delegate: "."+this.splitClass});
49692
49693         this.lockedHd.on("mouseover", this.handleHdOver, this);
49694         this.lockedHd.on("mouseout", this.handleHdOut, this);
49695         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49696                 {delegate: "."+this.splitClass});
49697
49698         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49699             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49700         }
49701
49702         this.updateSplitters();
49703
49704         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49705             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49706             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49707         }
49708
49709         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49710             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49711             this.hmenu.add(
49712                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49713                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49714             );
49715             if(this.grid.enableColLock !== false){
49716                 this.hmenu.add('-',
49717                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49718                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49719                 );
49720             }
49721             if(this.grid.enableColumnHide !== false){
49722
49723                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49724                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49725                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49726
49727                 this.hmenu.add('-',
49728                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49729                 );
49730             }
49731             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49732
49733             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49734         }
49735
49736         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49737             this.dd = new Roo.grid.GridDragZone(this.grid, {
49738                 ddGroup : this.grid.ddGroup || 'GridDD'
49739             });
49740         }
49741
49742         /*
49743         for(var i = 0; i < colCount; i++){
49744             if(cm.isHidden(i)){
49745                 this.hideColumn(i);
49746             }
49747             if(cm.config[i].align){
49748                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49749                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49750             }
49751         }*/
49752         
49753         this.updateHeaderSortState();
49754
49755         this.beforeInitialResize();
49756         this.layout(true);
49757
49758         // two part rendering gives faster view to the user
49759         this.renderPhase2.defer(1, this);
49760     },
49761
49762     renderPhase2 : function(){
49763         // render the rows now
49764         this.refresh();
49765         if(this.grid.autoSizeColumns){
49766             this.autoSizeColumns();
49767         }
49768     },
49769
49770     beforeInitialResize : function(){
49771
49772     },
49773
49774     onColumnSplitterMoved : function(i, w){
49775         this.userResized = true;
49776         var cm = this.grid.colModel;
49777         cm.setColumnWidth(i, w, true);
49778         var cid = cm.getColumnId(i);
49779         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49780         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49781         this.updateSplitters();
49782         this.layout();
49783         this.grid.fireEvent("columnresize", i, w);
49784     },
49785
49786     syncRowHeights : function(startIndex, endIndex){
49787         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49788             startIndex = startIndex || 0;
49789             var mrows = this.getBodyTable().rows;
49790             var lrows = this.getLockedTable().rows;
49791             var len = mrows.length-1;
49792             endIndex = Math.min(endIndex || len, len);
49793             for(var i = startIndex; i <= endIndex; i++){
49794                 var m = mrows[i], l = lrows[i];
49795                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49796                 m.style.height = l.style.height = h + "px";
49797             }
49798         }
49799     },
49800
49801     layout : function(initialRender, is2ndPass){
49802         var g = this.grid;
49803         var auto = g.autoHeight;
49804         var scrollOffset = 16;
49805         var c = g.getGridEl(), cm = this.cm,
49806                 expandCol = g.autoExpandColumn,
49807                 gv = this;
49808         //c.beginMeasure();
49809
49810         if(!c.dom.offsetWidth){ // display:none?
49811             if(initialRender){
49812                 this.lockedWrap.show();
49813                 this.mainWrap.show();
49814             }
49815             return;
49816         }
49817
49818         var hasLock = this.cm.isLocked(0);
49819
49820         var tbh = this.headerPanel.getHeight();
49821         var bbh = this.footerPanel.getHeight();
49822
49823         if(auto){
49824             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49825             var newHeight = ch + c.getBorderWidth("tb");
49826             if(g.maxHeight){
49827                 newHeight = Math.min(g.maxHeight, newHeight);
49828             }
49829             c.setHeight(newHeight);
49830         }
49831
49832         if(g.autoWidth){
49833             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49834         }
49835
49836         var s = this.scroller;
49837
49838         var csize = c.getSize(true);
49839
49840         this.el.setSize(csize.width, csize.height);
49841
49842         this.headerPanel.setWidth(csize.width);
49843         this.footerPanel.setWidth(csize.width);
49844
49845         var hdHeight = this.mainHd.getHeight();
49846         var vw = csize.width;
49847         var vh = csize.height - (tbh + bbh);
49848
49849         s.setSize(vw, vh);
49850
49851         var bt = this.getBodyTable();
49852         var ltWidth = hasLock ?
49853                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49854
49855         var scrollHeight = bt.offsetHeight;
49856         var scrollWidth = ltWidth + bt.offsetWidth;
49857         var vscroll = false, hscroll = false;
49858
49859         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49860
49861         var lw = this.lockedWrap, mw = this.mainWrap;
49862         var lb = this.lockedBody, mb = this.mainBody;
49863
49864         setTimeout(function(){
49865             var t = s.dom.offsetTop;
49866             var w = s.dom.clientWidth,
49867                 h = s.dom.clientHeight;
49868
49869             lw.setTop(t);
49870             lw.setSize(ltWidth, h);
49871
49872             mw.setLeftTop(ltWidth, t);
49873             mw.setSize(w-ltWidth, h);
49874
49875             lb.setHeight(h-hdHeight);
49876             mb.setHeight(h-hdHeight);
49877
49878             if(is2ndPass !== true && !gv.userResized && expandCol){
49879                 // high speed resize without full column calculation
49880                 
49881                 var ci = cm.getIndexById(expandCol);
49882                 if (ci < 0) {
49883                     ci = cm.findColumnIndex(expandCol);
49884                 }
49885                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49886                 var expandId = cm.getColumnId(ci);
49887                 var  tw = cm.getTotalWidth(false);
49888                 var currentWidth = cm.getColumnWidth(ci);
49889                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49890                 if(currentWidth != cw){
49891                     cm.setColumnWidth(ci, cw, true);
49892                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49893                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49894                     gv.updateSplitters();
49895                     gv.layout(false, true);
49896                 }
49897             }
49898
49899             if(initialRender){
49900                 lw.show();
49901                 mw.show();
49902             }
49903             //c.endMeasure();
49904         }, 10);
49905     },
49906
49907     onWindowResize : function(){
49908         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49909             return;
49910         }
49911         this.layout();
49912     },
49913
49914     appendFooter : function(parentEl){
49915         return null;
49916     },
49917
49918     sortAscText : "Sort Ascending",
49919     sortDescText : "Sort Descending",
49920     lockText : "Lock Column",
49921     unlockText : "Unlock Column",
49922     columnsText : "Columns"
49923 });
49924
49925
49926 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49927     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49928     this.proxy.el.addClass('x-grid3-col-dd');
49929 };
49930
49931 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49932     handleMouseDown : function(e){
49933
49934     },
49935
49936     callHandleMouseDown : function(e){
49937         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49938     }
49939 });
49940 /*
49941  * Based on:
49942  * Ext JS Library 1.1.1
49943  * Copyright(c) 2006-2007, Ext JS, LLC.
49944  *
49945  * Originally Released Under LGPL - original licence link has changed is not relivant.
49946  *
49947  * Fork - LGPL
49948  * <script type="text/javascript">
49949  */
49950  
49951 // private
49952 // This is a support class used internally by the Grid components
49953 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49954     this.grid = grid;
49955     this.view = grid.getView();
49956     this.proxy = this.view.resizeProxy;
49957     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49958         "gridSplitters" + this.grid.getGridEl().id, {
49959         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49960     });
49961     this.setHandleElId(Roo.id(hd));
49962     this.setOuterHandleElId(Roo.id(hd2));
49963     this.scroll = false;
49964 };
49965 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49966     fly: Roo.Element.fly,
49967
49968     b4StartDrag : function(x, y){
49969         this.view.headersDisabled = true;
49970         this.proxy.setHeight(this.view.mainWrap.getHeight());
49971         var w = this.cm.getColumnWidth(this.cellIndex);
49972         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49973         this.resetConstraints();
49974         this.setXConstraint(minw, 1000);
49975         this.setYConstraint(0, 0);
49976         this.minX = x - minw;
49977         this.maxX = x + 1000;
49978         this.startPos = x;
49979         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49980     },
49981
49982
49983     handleMouseDown : function(e){
49984         ev = Roo.EventObject.setEvent(e);
49985         var t = this.fly(ev.getTarget());
49986         if(t.hasClass("x-grid-split")){
49987             this.cellIndex = this.view.getCellIndex(t.dom);
49988             this.split = t.dom;
49989             this.cm = this.grid.colModel;
49990             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49991                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49992             }
49993         }
49994     },
49995
49996     endDrag : function(e){
49997         this.view.headersDisabled = false;
49998         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49999         var diff = endX - this.startPos;
50000         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50001     },
50002
50003     autoOffset : function(){
50004         this.setDelta(0,0);
50005     }
50006 });/*
50007  * Based on:
50008  * Ext JS Library 1.1.1
50009  * Copyright(c) 2006-2007, Ext JS, LLC.
50010  *
50011  * Originally Released Under LGPL - original licence link has changed is not relivant.
50012  *
50013  * Fork - LGPL
50014  * <script type="text/javascript">
50015  */
50016  
50017 // private
50018 // This is a support class used internally by the Grid components
50019 Roo.grid.GridDragZone = function(grid, config){
50020     this.view = grid.getView();
50021     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50022     if(this.view.lockedBody){
50023         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50024         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50025     }
50026     this.scroll = false;
50027     this.grid = grid;
50028     this.ddel = document.createElement('div');
50029     this.ddel.className = 'x-grid-dd-wrap';
50030 };
50031
50032 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50033     ddGroup : "GridDD",
50034
50035     getDragData : function(e){
50036         var t = Roo.lib.Event.getTarget(e);
50037         var rowIndex = this.view.findRowIndex(t);
50038         if(rowIndex !== false){
50039             var sm = this.grid.selModel;
50040             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50041               //  sm.mouseDown(e, t);
50042             //}
50043             if (e.hasModifier()){
50044                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50045             }
50046             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50047         }
50048         return false;
50049     },
50050
50051     onInitDrag : function(e){
50052         var data = this.dragData;
50053         this.ddel.innerHTML = this.grid.getDragDropText();
50054         this.proxy.update(this.ddel);
50055         // fire start drag?
50056     },
50057
50058     afterRepair : function(){
50059         this.dragging = false;
50060     },
50061
50062     getRepairXY : function(e, data){
50063         return false;
50064     },
50065
50066     onEndDrag : function(data, e){
50067         // fire end drag?
50068     },
50069
50070     onValidDrop : function(dd, e, id){
50071         // fire drag drop?
50072         this.hideProxy();
50073     },
50074
50075     beforeInvalidDrop : function(e, id){
50076
50077     }
50078 });/*
50079  * Based on:
50080  * Ext JS Library 1.1.1
50081  * Copyright(c) 2006-2007, Ext JS, LLC.
50082  *
50083  * Originally Released Under LGPL - original licence link has changed is not relivant.
50084  *
50085  * Fork - LGPL
50086  * <script type="text/javascript">
50087  */
50088  
50089
50090 /**
50091  * @class Roo.grid.ColumnModel
50092  * @extends Roo.util.Observable
50093  * This is the default implementation of a ColumnModel used by the Grid. It defines
50094  * the columns in the grid.
50095  * <br>Usage:<br>
50096  <pre><code>
50097  var colModel = new Roo.grid.ColumnModel([
50098         {header: "Ticker", width: 60, sortable: true, locked: true},
50099         {header: "Company Name", width: 150, sortable: true},
50100         {header: "Market Cap.", width: 100, sortable: true},
50101         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50102         {header: "Employees", width: 100, sortable: true, resizable: false}
50103  ]);
50104  </code></pre>
50105  * <p>
50106  
50107  * The config options listed for this class are options which may appear in each
50108  * individual column definition.
50109  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50110  * @constructor
50111  * @param {Object} config An Array of column config objects. See this class's
50112  * config objects for details.
50113 */
50114 Roo.grid.ColumnModel = function(config){
50115         /**
50116      * The config passed into the constructor
50117      */
50118     this.config = config;
50119     this.lookup = {};
50120
50121     // if no id, create one
50122     // if the column does not have a dataIndex mapping,
50123     // map it to the order it is in the config
50124     for(var i = 0, len = config.length; i < len; i++){
50125         var c = config[i];
50126         if(typeof c.dataIndex == "undefined"){
50127             c.dataIndex = i;
50128         }
50129         if(typeof c.renderer == "string"){
50130             c.renderer = Roo.util.Format[c.renderer];
50131         }
50132         if(typeof c.id == "undefined"){
50133             c.id = Roo.id();
50134         }
50135         if(c.editor && c.editor.xtype){
50136             c.editor  = Roo.factory(c.editor, Roo.grid);
50137         }
50138         if(c.editor && c.editor.isFormField){
50139             c.editor = new Roo.grid.GridEditor(c.editor);
50140         }
50141         this.lookup[c.id] = c;
50142     }
50143
50144     /**
50145      * The width of columns which have no width specified (defaults to 100)
50146      * @type Number
50147      */
50148     this.defaultWidth = 100;
50149
50150     /**
50151      * Default sortable of columns which have no sortable specified (defaults to false)
50152      * @type Boolean
50153      */
50154     this.defaultSortable = false;
50155
50156     this.addEvents({
50157         /**
50158              * @event widthchange
50159              * Fires when the width of a column changes.
50160              * @param {ColumnModel} this
50161              * @param {Number} columnIndex The column index
50162              * @param {Number} newWidth The new width
50163              */
50164             "widthchange": true,
50165         /**
50166              * @event headerchange
50167              * Fires when the text of a header changes.
50168              * @param {ColumnModel} this
50169              * @param {Number} columnIndex The column index
50170              * @param {Number} newText The new header text
50171              */
50172             "headerchange": true,
50173         /**
50174              * @event hiddenchange
50175              * Fires when a column is hidden or "unhidden".
50176              * @param {ColumnModel} this
50177              * @param {Number} columnIndex The column index
50178              * @param {Boolean} hidden true if hidden, false otherwise
50179              */
50180             "hiddenchange": true,
50181             /**
50182          * @event columnmoved
50183          * Fires when a column is moved.
50184          * @param {ColumnModel} this
50185          * @param {Number} oldIndex
50186          * @param {Number} newIndex
50187          */
50188         "columnmoved" : true,
50189         /**
50190          * @event columlockchange
50191          * Fires when a column's locked state is changed
50192          * @param {ColumnModel} this
50193          * @param {Number} colIndex
50194          * @param {Boolean} locked true if locked
50195          */
50196         "columnlockchange" : true
50197     });
50198     Roo.grid.ColumnModel.superclass.constructor.call(this);
50199 };
50200 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50201     /**
50202      * @cfg {String} header The header text to display in the Grid view.
50203      */
50204     /**
50205      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50206      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50207      * specified, the column's index is used as an index into the Record's data Array.
50208      */
50209     /**
50210      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50211      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50212      */
50213     /**
50214      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50215      * Defaults to the value of the {@link #defaultSortable} property.
50216      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50217      */
50218     /**
50219      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50220      */
50221     /**
50222      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50223      */
50224     /**
50225      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50226      */
50227     /**
50228      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50229      */
50230     /**
50231      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50232      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50233      * default renderer uses the raw data value.
50234      */
50235        /**
50236      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50237      */
50238     /**
50239      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50240      */
50241
50242     /**
50243      * Returns the id of the column at the specified index.
50244      * @param {Number} index The column index
50245      * @return {String} the id
50246      */
50247     getColumnId : function(index){
50248         return this.config[index].id;
50249     },
50250
50251     /**
50252      * Returns the column for a specified id.
50253      * @param {String} id The column id
50254      * @return {Object} the column
50255      */
50256     getColumnById : function(id){
50257         return this.lookup[id];
50258     },
50259
50260     
50261     /**
50262      * Returns the column for a specified dataIndex.
50263      * @param {String} dataIndex The column dataIndex
50264      * @return {Object|Boolean} the column or false if not found
50265      */
50266     getColumnByDataIndex: function(dataIndex){
50267         var index = this.findColumnIndex(dataIndex);
50268         return index > -1 ? this.config[index] : false;
50269     },
50270     
50271     /**
50272      * Returns the index for a specified column id.
50273      * @param {String} id The column id
50274      * @return {Number} the index, or -1 if not found
50275      */
50276     getIndexById : function(id){
50277         for(var i = 0, len = this.config.length; i < len; i++){
50278             if(this.config[i].id == id){
50279                 return i;
50280             }
50281         }
50282         return -1;
50283     },
50284     
50285     /**
50286      * Returns the index for a specified column dataIndex.
50287      * @param {String} dataIndex The column dataIndex
50288      * @return {Number} the index, or -1 if not found
50289      */
50290     
50291     findColumnIndex : function(dataIndex){
50292         for(var i = 0, len = this.config.length; i < len; i++){
50293             if(this.config[i].dataIndex == dataIndex){
50294                 return i;
50295             }
50296         }
50297         return -1;
50298     },
50299     
50300     
50301     moveColumn : function(oldIndex, newIndex){
50302         var c = this.config[oldIndex];
50303         this.config.splice(oldIndex, 1);
50304         this.config.splice(newIndex, 0, c);
50305         this.dataMap = null;
50306         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50307     },
50308
50309     isLocked : function(colIndex){
50310         return this.config[colIndex].locked === true;
50311     },
50312
50313     setLocked : function(colIndex, value, suppressEvent){
50314         if(this.isLocked(colIndex) == value){
50315             return;
50316         }
50317         this.config[colIndex].locked = value;
50318         if(!suppressEvent){
50319             this.fireEvent("columnlockchange", this, colIndex, value);
50320         }
50321     },
50322
50323     getTotalLockedWidth : function(){
50324         var totalWidth = 0;
50325         for(var i = 0; i < this.config.length; i++){
50326             if(this.isLocked(i) && !this.isHidden(i)){
50327                 this.totalWidth += this.getColumnWidth(i);
50328             }
50329         }
50330         return totalWidth;
50331     },
50332
50333     getLockedCount : function(){
50334         for(var i = 0, len = this.config.length; i < len; i++){
50335             if(!this.isLocked(i)){
50336                 return i;
50337             }
50338         }
50339     },
50340
50341     /**
50342      * Returns the number of columns.
50343      * @return {Number}
50344      */
50345     getColumnCount : function(visibleOnly){
50346         if(visibleOnly === true){
50347             var c = 0;
50348             for(var i = 0, len = this.config.length; i < len; i++){
50349                 if(!this.isHidden(i)){
50350                     c++;
50351                 }
50352             }
50353             return c;
50354         }
50355         return this.config.length;
50356     },
50357
50358     /**
50359      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50360      * @param {Function} fn
50361      * @param {Object} scope (optional)
50362      * @return {Array} result
50363      */
50364     getColumnsBy : function(fn, scope){
50365         var r = [];
50366         for(var i = 0, len = this.config.length; i < len; i++){
50367             var c = this.config[i];
50368             if(fn.call(scope||this, c, i) === true){
50369                 r[r.length] = c;
50370             }
50371         }
50372         return r;
50373     },
50374
50375     /**
50376      * Returns true if the specified column is sortable.
50377      * @param {Number} col The column index
50378      * @return {Boolean}
50379      */
50380     isSortable : function(col){
50381         if(typeof this.config[col].sortable == "undefined"){
50382             return this.defaultSortable;
50383         }
50384         return this.config[col].sortable;
50385     },
50386
50387     /**
50388      * Returns the rendering (formatting) function defined for the column.
50389      * @param {Number} col The column index.
50390      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50391      */
50392     getRenderer : function(col){
50393         if(!this.config[col].renderer){
50394             return Roo.grid.ColumnModel.defaultRenderer;
50395         }
50396         return this.config[col].renderer;
50397     },
50398
50399     /**
50400      * Sets the rendering (formatting) function for a column.
50401      * @param {Number} col The column index
50402      * @param {Function} fn The function to use to process the cell's raw data
50403      * to return HTML markup for the grid view. The render function is called with
50404      * the following parameters:<ul>
50405      * <li>Data value.</li>
50406      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50407      * <li>css A CSS style string to apply to the table cell.</li>
50408      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50409      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50410      * <li>Row index</li>
50411      * <li>Column index</li>
50412      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50413      */
50414     setRenderer : function(col, fn){
50415         this.config[col].renderer = fn;
50416     },
50417
50418     /**
50419      * Returns the width for the specified column.
50420      * @param {Number} col The column index
50421      * @return {Number}
50422      */
50423     getColumnWidth : function(col){
50424         return this.config[col].width * 1 || this.defaultWidth;
50425     },
50426
50427     /**
50428      * Sets the width for a column.
50429      * @param {Number} col The column index
50430      * @param {Number} width The new width
50431      */
50432     setColumnWidth : function(col, width, suppressEvent){
50433         this.config[col].width = width;
50434         this.totalWidth = null;
50435         if(!suppressEvent){
50436              this.fireEvent("widthchange", this, col, width);
50437         }
50438     },
50439
50440     /**
50441      * Returns the total width of all columns.
50442      * @param {Boolean} includeHidden True to include hidden column widths
50443      * @return {Number}
50444      */
50445     getTotalWidth : function(includeHidden){
50446         if(!this.totalWidth){
50447             this.totalWidth = 0;
50448             for(var i = 0, len = this.config.length; i < len; i++){
50449                 if(includeHidden || !this.isHidden(i)){
50450                     this.totalWidth += this.getColumnWidth(i);
50451                 }
50452             }
50453         }
50454         return this.totalWidth;
50455     },
50456
50457     /**
50458      * Returns the header for the specified column.
50459      * @param {Number} col The column index
50460      * @return {String}
50461      */
50462     getColumnHeader : function(col){
50463         return this.config[col].header;
50464     },
50465
50466     /**
50467      * Sets the header for a column.
50468      * @param {Number} col The column index
50469      * @param {String} header The new header
50470      */
50471     setColumnHeader : function(col, header){
50472         this.config[col].header = header;
50473         this.fireEvent("headerchange", this, col, header);
50474     },
50475
50476     /**
50477      * Returns the tooltip for the specified column.
50478      * @param {Number} col The column index
50479      * @return {String}
50480      */
50481     getColumnTooltip : function(col){
50482             return this.config[col].tooltip;
50483     },
50484     /**
50485      * Sets the tooltip for a column.
50486      * @param {Number} col The column index
50487      * @param {String} tooltip The new tooltip
50488      */
50489     setColumnTooltip : function(col, tooltip){
50490             this.config[col].tooltip = tooltip;
50491     },
50492
50493     /**
50494      * Returns the dataIndex for the specified column.
50495      * @param {Number} col The column index
50496      * @return {Number}
50497      */
50498     getDataIndex : function(col){
50499         return this.config[col].dataIndex;
50500     },
50501
50502     /**
50503      * Sets the dataIndex for a column.
50504      * @param {Number} col The column index
50505      * @param {Number} dataIndex The new dataIndex
50506      */
50507     setDataIndex : function(col, dataIndex){
50508         this.config[col].dataIndex = dataIndex;
50509     },
50510
50511     
50512     
50513     /**
50514      * Returns true if the cell is editable.
50515      * @param {Number} colIndex The column index
50516      * @param {Number} rowIndex The row index
50517      * @return {Boolean}
50518      */
50519     isCellEditable : function(colIndex, rowIndex){
50520         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50521     },
50522
50523     /**
50524      * Returns the editor defined for the cell/column.
50525      * return false or null to disable editing.
50526      * @param {Number} colIndex The column index
50527      * @param {Number} rowIndex The row index
50528      * @return {Object}
50529      */
50530     getCellEditor : function(colIndex, rowIndex){
50531         return this.config[colIndex].editor;
50532     },
50533
50534     /**
50535      * Sets if a column is editable.
50536      * @param {Number} col The column index
50537      * @param {Boolean} editable True if the column is editable
50538      */
50539     setEditable : function(col, editable){
50540         this.config[col].editable = editable;
50541     },
50542
50543
50544     /**
50545      * Returns true if the column is hidden.
50546      * @param {Number} colIndex The column index
50547      * @return {Boolean}
50548      */
50549     isHidden : function(colIndex){
50550         return this.config[colIndex].hidden;
50551     },
50552
50553
50554     /**
50555      * Returns true if the column width cannot be changed
50556      */
50557     isFixed : function(colIndex){
50558         return this.config[colIndex].fixed;
50559     },
50560
50561     /**
50562      * Returns true if the column can be resized
50563      * @return {Boolean}
50564      */
50565     isResizable : function(colIndex){
50566         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50567     },
50568     /**
50569      * Sets if a column is hidden.
50570      * @param {Number} colIndex The column index
50571      * @param {Boolean} hidden True if the column is hidden
50572      */
50573     setHidden : function(colIndex, hidden){
50574         this.config[colIndex].hidden = hidden;
50575         this.totalWidth = null;
50576         this.fireEvent("hiddenchange", this, colIndex, hidden);
50577     },
50578
50579     /**
50580      * Sets the editor for a column.
50581      * @param {Number} col The column index
50582      * @param {Object} editor The editor object
50583      */
50584     setEditor : function(col, editor){
50585         this.config[col].editor = editor;
50586     }
50587 });
50588
50589 Roo.grid.ColumnModel.defaultRenderer = function(value){
50590         if(typeof value == "string" && value.length < 1){
50591             return "&#160;";
50592         }
50593         return value;
50594 };
50595
50596 // Alias for backwards compatibility
50597 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50598 /*
50599  * Based on:
50600  * Ext JS Library 1.1.1
50601  * Copyright(c) 2006-2007, Ext JS, LLC.
50602  *
50603  * Originally Released Under LGPL - original licence link has changed is not relivant.
50604  *
50605  * Fork - LGPL
50606  * <script type="text/javascript">
50607  */
50608
50609 /**
50610  * @class Roo.grid.AbstractSelectionModel
50611  * @extends Roo.util.Observable
50612  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50613  * implemented by descendant classes.  This class should not be directly instantiated.
50614  * @constructor
50615  */
50616 Roo.grid.AbstractSelectionModel = function(){
50617     this.locked = false;
50618     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50619 };
50620
50621 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50622     /** @ignore Called by the grid automatically. Do not call directly. */
50623     init : function(grid){
50624         this.grid = grid;
50625         this.initEvents();
50626     },
50627
50628     /**
50629      * Locks the selections.
50630      */
50631     lock : function(){
50632         this.locked = true;
50633     },
50634
50635     /**
50636      * Unlocks the selections.
50637      */
50638     unlock : function(){
50639         this.locked = false;
50640     },
50641
50642     /**
50643      * Returns true if the selections are locked.
50644      * @return {Boolean}
50645      */
50646     isLocked : function(){
50647         return this.locked;
50648     }
50649 });/*
50650  * Based on:
50651  * Ext JS Library 1.1.1
50652  * Copyright(c) 2006-2007, Ext JS, LLC.
50653  *
50654  * Originally Released Under LGPL - original licence link has changed is not relivant.
50655  *
50656  * Fork - LGPL
50657  * <script type="text/javascript">
50658  */
50659 /**
50660  * @extends Roo.grid.AbstractSelectionModel
50661  * @class Roo.grid.RowSelectionModel
50662  * The default SelectionModel used by {@link Roo.grid.Grid}.
50663  * It supports multiple selections and keyboard selection/navigation. 
50664  * @constructor
50665  * @param {Object} config
50666  */
50667 Roo.grid.RowSelectionModel = function(config){
50668     Roo.apply(this, config);
50669     this.selections = new Roo.util.MixedCollection(false, function(o){
50670         return o.id;
50671     });
50672
50673     this.last = false;
50674     this.lastActive = false;
50675
50676     this.addEvents({
50677         /**
50678              * @event selectionchange
50679              * Fires when the selection changes
50680              * @param {SelectionModel} this
50681              */
50682             "selectionchange" : true,
50683         /**
50684              * @event afterselectionchange
50685              * Fires after the selection changes (eg. by key press or clicking)
50686              * @param {SelectionModel} this
50687              */
50688             "afterselectionchange" : true,
50689         /**
50690              * @event beforerowselect
50691              * Fires when a row is selected being selected, return false to cancel.
50692              * @param {SelectionModel} this
50693              * @param {Number} rowIndex The selected index
50694              * @param {Boolean} keepExisting False if other selections will be cleared
50695              */
50696             "beforerowselect" : true,
50697         /**
50698              * @event rowselect
50699              * Fires when a row is selected.
50700              * @param {SelectionModel} this
50701              * @param {Number} rowIndex The selected index
50702              * @param {Roo.data.Record} r The record
50703              */
50704             "rowselect" : true,
50705         /**
50706              * @event rowdeselect
50707              * Fires when a row is deselected.
50708              * @param {SelectionModel} this
50709              * @param {Number} rowIndex The selected index
50710              */
50711         "rowdeselect" : true
50712     });
50713     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50714     this.locked = false;
50715 };
50716
50717 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50718     /**
50719      * @cfg {Boolean} singleSelect
50720      * True to allow selection of only one row at a time (defaults to false)
50721      */
50722     singleSelect : false,
50723
50724     // private
50725     initEvents : function(){
50726
50727         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50728             this.grid.on("mousedown", this.handleMouseDown, this);
50729         }else{ // allow click to work like normal
50730             this.grid.on("rowclick", this.handleDragableRowClick, this);
50731         }
50732
50733         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50734             "up" : function(e){
50735                 if(!e.shiftKey){
50736                     this.selectPrevious(e.shiftKey);
50737                 }else if(this.last !== false && this.lastActive !== false){
50738                     var last = this.last;
50739                     this.selectRange(this.last,  this.lastActive-1);
50740                     this.grid.getView().focusRow(this.lastActive);
50741                     if(last !== false){
50742                         this.last = last;
50743                     }
50744                 }else{
50745                     this.selectFirstRow();
50746                 }
50747                 this.fireEvent("afterselectionchange", this);
50748             },
50749             "down" : function(e){
50750                 if(!e.shiftKey){
50751                     this.selectNext(e.shiftKey);
50752                 }else if(this.last !== false && this.lastActive !== false){
50753                     var last = this.last;
50754                     this.selectRange(this.last,  this.lastActive+1);
50755                     this.grid.getView().focusRow(this.lastActive);
50756                     if(last !== false){
50757                         this.last = last;
50758                     }
50759                 }else{
50760                     this.selectFirstRow();
50761                 }
50762                 this.fireEvent("afterselectionchange", this);
50763             },
50764             scope: this
50765         });
50766
50767         var view = this.grid.view;
50768         view.on("refresh", this.onRefresh, this);
50769         view.on("rowupdated", this.onRowUpdated, this);
50770         view.on("rowremoved", this.onRemove, this);
50771     },
50772
50773     // private
50774     onRefresh : function(){
50775         var ds = this.grid.dataSource, i, v = this.grid.view;
50776         var s = this.selections;
50777         s.each(function(r){
50778             if((i = ds.indexOfId(r.id)) != -1){
50779                 v.onRowSelect(i);
50780             }else{
50781                 s.remove(r);
50782             }
50783         });
50784     },
50785
50786     // private
50787     onRemove : function(v, index, r){
50788         this.selections.remove(r);
50789     },
50790
50791     // private
50792     onRowUpdated : function(v, index, r){
50793         if(this.isSelected(r)){
50794             v.onRowSelect(index);
50795         }
50796     },
50797
50798     /**
50799      * Select records.
50800      * @param {Array} records The records to select
50801      * @param {Boolean} keepExisting (optional) True to keep existing selections
50802      */
50803     selectRecords : function(records, keepExisting){
50804         if(!keepExisting){
50805             this.clearSelections();
50806         }
50807         var ds = this.grid.dataSource;
50808         for(var i = 0, len = records.length; i < len; i++){
50809             this.selectRow(ds.indexOf(records[i]), true);
50810         }
50811     },
50812
50813     /**
50814      * Gets the number of selected rows.
50815      * @return {Number}
50816      */
50817     getCount : function(){
50818         return this.selections.length;
50819     },
50820
50821     /**
50822      * Selects the first row in the grid.
50823      */
50824     selectFirstRow : function(){
50825         this.selectRow(0);
50826     },
50827
50828     /**
50829      * Select the last row.
50830      * @param {Boolean} keepExisting (optional) True to keep existing selections
50831      */
50832     selectLastRow : function(keepExisting){
50833         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50834     },
50835
50836     /**
50837      * Selects the row immediately following the last selected row.
50838      * @param {Boolean} keepExisting (optional) True to keep existing selections
50839      */
50840     selectNext : function(keepExisting){
50841         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50842             this.selectRow(this.last+1, keepExisting);
50843             this.grid.getView().focusRow(this.last);
50844         }
50845     },
50846
50847     /**
50848      * Selects the row that precedes the last selected row.
50849      * @param {Boolean} keepExisting (optional) True to keep existing selections
50850      */
50851     selectPrevious : function(keepExisting){
50852         if(this.last){
50853             this.selectRow(this.last-1, keepExisting);
50854             this.grid.getView().focusRow(this.last);
50855         }
50856     },
50857
50858     /**
50859      * Returns the selected records
50860      * @return {Array} Array of selected records
50861      */
50862     getSelections : function(){
50863         return [].concat(this.selections.items);
50864     },
50865
50866     /**
50867      * Returns the first selected record.
50868      * @return {Record}
50869      */
50870     getSelected : function(){
50871         return this.selections.itemAt(0);
50872     },
50873
50874
50875     /**
50876      * Clears all selections.
50877      */
50878     clearSelections : function(fast){
50879         if(this.locked) return;
50880         if(fast !== true){
50881             var ds = this.grid.dataSource;
50882             var s = this.selections;
50883             s.each(function(r){
50884                 this.deselectRow(ds.indexOfId(r.id));
50885             }, this);
50886             s.clear();
50887         }else{
50888             this.selections.clear();
50889         }
50890         this.last = false;
50891     },
50892
50893
50894     /**
50895      * Selects all rows.
50896      */
50897     selectAll : function(){
50898         if(this.locked) return;
50899         this.selections.clear();
50900         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50901             this.selectRow(i, true);
50902         }
50903     },
50904
50905     /**
50906      * Returns True if there is a selection.
50907      * @return {Boolean}
50908      */
50909     hasSelection : function(){
50910         return this.selections.length > 0;
50911     },
50912
50913     /**
50914      * Returns True if the specified row is selected.
50915      * @param {Number/Record} record The record or index of the record to check
50916      * @return {Boolean}
50917      */
50918     isSelected : function(index){
50919         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50920         return (r && this.selections.key(r.id) ? true : false);
50921     },
50922
50923     /**
50924      * Returns True if the specified record id is selected.
50925      * @param {String} id The id of record to check
50926      * @return {Boolean}
50927      */
50928     isIdSelected : function(id){
50929         return (this.selections.key(id) ? true : false);
50930     },
50931
50932     // private
50933     handleMouseDown : function(e, t){
50934         var view = this.grid.getView(), rowIndex;
50935         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50936             return;
50937         };
50938         if(e.shiftKey && this.last !== false){
50939             var last = this.last;
50940             this.selectRange(last, rowIndex, e.ctrlKey);
50941             this.last = last; // reset the last
50942             view.focusRow(rowIndex);
50943         }else{
50944             var isSelected = this.isSelected(rowIndex);
50945             if(e.button !== 0 && isSelected){
50946                 view.focusRow(rowIndex);
50947             }else if(e.ctrlKey && isSelected){
50948                 this.deselectRow(rowIndex);
50949             }else if(!isSelected){
50950                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50951                 view.focusRow(rowIndex);
50952             }
50953         }
50954         this.fireEvent("afterselectionchange", this);
50955     },
50956     // private
50957     handleDragableRowClick :  function(grid, rowIndex, e) 
50958     {
50959         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50960             this.selectRow(rowIndex, false);
50961             grid.view.focusRow(rowIndex);
50962              this.fireEvent("afterselectionchange", this);
50963         }
50964     },
50965     
50966     /**
50967      * Selects multiple rows.
50968      * @param {Array} rows Array of the indexes of the row to select
50969      * @param {Boolean} keepExisting (optional) True to keep existing selections
50970      */
50971     selectRows : function(rows, keepExisting){
50972         if(!keepExisting){
50973             this.clearSelections();
50974         }
50975         for(var i = 0, len = rows.length; i < len; i++){
50976             this.selectRow(rows[i], true);
50977         }
50978     },
50979
50980     /**
50981      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50982      * @param {Number} startRow The index of the first row in the range
50983      * @param {Number} endRow The index of the last row in the range
50984      * @param {Boolean} keepExisting (optional) True to retain existing selections
50985      */
50986     selectRange : function(startRow, endRow, keepExisting){
50987         if(this.locked) return;
50988         if(!keepExisting){
50989             this.clearSelections();
50990         }
50991         if(startRow <= endRow){
50992             for(var i = startRow; i <= endRow; i++){
50993                 this.selectRow(i, true);
50994             }
50995         }else{
50996             for(var i = startRow; i >= endRow; i--){
50997                 this.selectRow(i, true);
50998             }
50999         }
51000     },
51001
51002     /**
51003      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51004      * @param {Number} startRow The index of the first row in the range
51005      * @param {Number} endRow The index of the last row in the range
51006      */
51007     deselectRange : function(startRow, endRow, preventViewNotify){
51008         if(this.locked) return;
51009         for(var i = startRow; i <= endRow; i++){
51010             this.deselectRow(i, preventViewNotify);
51011         }
51012     },
51013
51014     /**
51015      * Selects a row.
51016      * @param {Number} row The index of the row to select
51017      * @param {Boolean} keepExisting (optional) True to keep existing selections
51018      */
51019     selectRow : function(index, keepExisting, preventViewNotify){
51020         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51021         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51022             if(!keepExisting || this.singleSelect){
51023                 this.clearSelections();
51024             }
51025             var r = this.grid.dataSource.getAt(index);
51026             this.selections.add(r);
51027             this.last = this.lastActive = index;
51028             if(!preventViewNotify){
51029                 this.grid.getView().onRowSelect(index);
51030             }
51031             this.fireEvent("rowselect", this, index, r);
51032             this.fireEvent("selectionchange", this);
51033         }
51034     },
51035
51036     /**
51037      * Deselects a row.
51038      * @param {Number} row The index of the row to deselect
51039      */
51040     deselectRow : function(index, preventViewNotify){
51041         if(this.locked) return;
51042         if(this.last == index){
51043             this.last = false;
51044         }
51045         if(this.lastActive == index){
51046             this.lastActive = false;
51047         }
51048         var r = this.grid.dataSource.getAt(index);
51049         this.selections.remove(r);
51050         if(!preventViewNotify){
51051             this.grid.getView().onRowDeselect(index);
51052         }
51053         this.fireEvent("rowdeselect", this, index);
51054         this.fireEvent("selectionchange", this);
51055     },
51056
51057     // private
51058     restoreLast : function(){
51059         if(this._last){
51060             this.last = this._last;
51061         }
51062     },
51063
51064     // private
51065     acceptsNav : function(row, col, cm){
51066         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51067     },
51068
51069     // private
51070     onEditorKey : function(field, e){
51071         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51072         if(k == e.TAB){
51073             e.stopEvent();
51074             ed.completeEdit();
51075             if(e.shiftKey){
51076                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51077             }else{
51078                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51079             }
51080         }else if(k == e.ENTER && !e.ctrlKey){
51081             e.stopEvent();
51082             ed.completeEdit();
51083             if(e.shiftKey){
51084                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51085             }else{
51086                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51087             }
51088         }else if(k == e.ESC){
51089             ed.cancelEdit();
51090         }
51091         if(newCell){
51092             g.startEditing(newCell[0], newCell[1]);
51093         }
51094     }
51095 });/*
51096  * Based on:
51097  * Ext JS Library 1.1.1
51098  * Copyright(c) 2006-2007, Ext JS, LLC.
51099  *
51100  * Originally Released Under LGPL - original licence link has changed is not relivant.
51101  *
51102  * Fork - LGPL
51103  * <script type="text/javascript">
51104  */
51105 /**
51106  * @class Roo.grid.CellSelectionModel
51107  * @extends Roo.grid.AbstractSelectionModel
51108  * This class provides the basic implementation for cell selection in a grid.
51109  * @constructor
51110  * @param {Object} config The object containing the configuration of this model.
51111  */
51112 Roo.grid.CellSelectionModel = function(config){
51113     Roo.apply(this, config);
51114
51115     this.selection = null;
51116
51117     this.addEvents({
51118         /**
51119              * @event beforerowselect
51120              * Fires before a cell is selected.
51121              * @param {SelectionModel} this
51122              * @param {Number} rowIndex The selected row index
51123              * @param {Number} colIndex The selected cell index
51124              */
51125             "beforecellselect" : true,
51126         /**
51127              * @event cellselect
51128              * Fires when a cell is selected.
51129              * @param {SelectionModel} this
51130              * @param {Number} rowIndex The selected row index
51131              * @param {Number} colIndex The selected cell index
51132              */
51133             "cellselect" : true,
51134         /**
51135              * @event selectionchange
51136              * Fires when the active selection changes.
51137              * @param {SelectionModel} this
51138              * @param {Object} selection null for no selection or an object (o) with two properties
51139                 <ul>
51140                 <li>o.record: the record object for the row the selection is in</li>
51141                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51142                 </ul>
51143              */
51144             "selectionchange" : true
51145     });
51146     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51147 };
51148
51149 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51150
51151     /** @ignore */
51152     initEvents : function(){
51153         this.grid.on("mousedown", this.handleMouseDown, this);
51154         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51155         var view = this.grid.view;
51156         view.on("refresh", this.onViewChange, this);
51157         view.on("rowupdated", this.onRowUpdated, this);
51158         view.on("beforerowremoved", this.clearSelections, this);
51159         view.on("beforerowsinserted", this.clearSelections, this);
51160         if(this.grid.isEditor){
51161             this.grid.on("beforeedit", this.beforeEdit,  this);
51162         }
51163     },
51164
51165         //private
51166     beforeEdit : function(e){
51167         this.select(e.row, e.column, false, true, e.record);
51168     },
51169
51170         //private
51171     onRowUpdated : function(v, index, r){
51172         if(this.selection && this.selection.record == r){
51173             v.onCellSelect(index, this.selection.cell[1]);
51174         }
51175     },
51176
51177         //private
51178     onViewChange : function(){
51179         this.clearSelections(true);
51180     },
51181
51182         /**
51183          * Returns the currently selected cell,.
51184          * @return {Array} The selected cell (row, column) or null if none selected.
51185          */
51186     getSelectedCell : function(){
51187         return this.selection ? this.selection.cell : null;
51188     },
51189
51190     /**
51191      * Clears all selections.
51192      * @param {Boolean} true to prevent the gridview from being notified about the change.
51193      */
51194     clearSelections : function(preventNotify){
51195         var s = this.selection;
51196         if(s){
51197             if(preventNotify !== true){
51198                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51199             }
51200             this.selection = null;
51201             this.fireEvent("selectionchange", this, null);
51202         }
51203     },
51204
51205     /**
51206      * Returns true if there is a selection.
51207      * @return {Boolean}
51208      */
51209     hasSelection : function(){
51210         return this.selection ? true : false;
51211     },
51212
51213     /** @ignore */
51214     handleMouseDown : function(e, t){
51215         var v = this.grid.getView();
51216         if(this.isLocked()){
51217             return;
51218         };
51219         var row = v.findRowIndex(t);
51220         var cell = v.findCellIndex(t);
51221         if(row !== false && cell !== false){
51222             this.select(row, cell);
51223         }
51224     },
51225
51226     /**
51227      * Selects a cell.
51228      * @param {Number} rowIndex
51229      * @param {Number} collIndex
51230      */
51231     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51232         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51233             this.clearSelections();
51234             r = r || this.grid.dataSource.getAt(rowIndex);
51235             this.selection = {
51236                 record : r,
51237                 cell : [rowIndex, colIndex]
51238             };
51239             if(!preventViewNotify){
51240                 var v = this.grid.getView();
51241                 v.onCellSelect(rowIndex, colIndex);
51242                 if(preventFocus !== true){
51243                     v.focusCell(rowIndex, colIndex);
51244                 }
51245             }
51246             this.fireEvent("cellselect", this, rowIndex, colIndex);
51247             this.fireEvent("selectionchange", this, this.selection);
51248         }
51249     },
51250
51251         //private
51252     isSelectable : function(rowIndex, colIndex, cm){
51253         return !cm.isHidden(colIndex);
51254     },
51255
51256     /** @ignore */
51257     handleKeyDown : function(e){
51258         //Roo.log('Cell Sel Model handleKeyDown');
51259         if(!e.isNavKeyPress()){
51260             return;
51261         }
51262         var g = this.grid, s = this.selection;
51263         if(!s){
51264             e.stopEvent();
51265             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51266             if(cell){
51267                 this.select(cell[0], cell[1]);
51268             }
51269             return;
51270         }
51271         var sm = this;
51272         var walk = function(row, col, step){
51273             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51274         };
51275         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51276         var newCell;
51277
51278         switch(k){
51279             case e.TAB:
51280                 // handled by onEditorKey
51281                 if (g.isEditor && g.editing) {
51282                     return;
51283                 }
51284                 if(e.shiftKey){
51285                      newCell = walk(r, c-1, -1);
51286                 }else{
51287                      newCell = walk(r, c+1, 1);
51288                 }
51289              break;
51290              case e.DOWN:
51291                  newCell = walk(r+1, c, 1);
51292              break;
51293              case e.UP:
51294                  newCell = walk(r-1, c, -1);
51295              break;
51296              case e.RIGHT:
51297                  newCell = walk(r, c+1, 1);
51298              break;
51299              case e.LEFT:
51300                  newCell = walk(r, c-1, -1);
51301              break;
51302              case e.ENTER:
51303                  if(g.isEditor && !g.editing){
51304                     g.startEditing(r, c);
51305                     e.stopEvent();
51306                     return;
51307                 }
51308              break;
51309         };
51310         if(newCell){
51311             this.select(newCell[0], newCell[1]);
51312             e.stopEvent();
51313         }
51314     },
51315
51316     acceptsNav : function(row, col, cm){
51317         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51318     },
51319     /**
51320      * Selects a cell.
51321      * @param {Number} field (not used) - as it's normally used as a listener
51322      * @param {Number} e - event - fake it by using
51323      *
51324      * var e = Roo.EventObjectImpl.prototype;
51325      * e.keyCode = e.TAB
51326      *
51327      * 
51328      */
51329     onEditorKey : function(field, e){
51330         
51331         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51332         ///Roo.log('onEditorKey' + k);
51333         if (!ed) {
51334             
51335             
51336             
51337         }
51338         if(k == e.TAB){
51339             if(e.shiftKey){
51340                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51341             }else{
51342                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51343             }
51344             
51345             e.stopEvent();
51346             
51347         }else if(k == e.ENTER &&  !e.ctrlKey){
51348             ed.completeEdit();
51349             e.stopEvent();
51350             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51351         }else if(k == e.ESC){
51352             ed.cancelEdit();
51353         }
51354         
51355         
51356         if(newCell){
51357             //Roo.log('next cell after edit');
51358             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51359         }
51360     }
51361 });/*
51362  * Based on:
51363  * Ext JS Library 1.1.1
51364  * Copyright(c) 2006-2007, Ext JS, LLC.
51365  *
51366  * Originally Released Under LGPL - original licence link has changed is not relivant.
51367  *
51368  * Fork - LGPL
51369  * <script type="text/javascript">
51370  */
51371  
51372 /**
51373  * @class Roo.grid.EditorGrid
51374  * @extends Roo.grid.Grid
51375  * Class for creating and editable grid.
51376  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51377  * The container MUST have some type of size defined for the grid to fill. The container will be 
51378  * automatically set to position relative if it isn't already.
51379  * @param {Object} dataSource The data model to bind to
51380  * @param {Object} colModel The column model with info about this grid's columns
51381  */
51382 Roo.grid.EditorGrid = function(container, config){
51383     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51384     this.getGridEl().addClass("xedit-grid");
51385
51386     if(!this.selModel){
51387         this.selModel = new Roo.grid.CellSelectionModel();
51388     }
51389
51390     this.activeEditor = null;
51391
51392         this.addEvents({
51393             /**
51394              * @event beforeedit
51395              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51396              * <ul style="padding:5px;padding-left:16px;">
51397              * <li>grid - This grid</li>
51398              * <li>record - The record being edited</li>
51399              * <li>field - The field name being edited</li>
51400              * <li>value - The value for the field being edited.</li>
51401              * <li>row - The grid row index</li>
51402              * <li>column - The grid column index</li>
51403              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51404              * </ul>
51405              * @param {Object} e An edit event (see above for description)
51406              */
51407             "beforeedit" : true,
51408             /**
51409              * @event afteredit
51410              * Fires after a cell is edited. <br />
51411              * <ul style="padding:5px;padding-left:16px;">
51412              * <li>grid - This grid</li>
51413              * <li>record - The record being edited</li>
51414              * <li>field - The field name being edited</li>
51415              * <li>value - The value being set</li>
51416              * <li>originalValue - The original value for the field, before the edit.</li>
51417              * <li>row - The grid row index</li>
51418              * <li>column - The grid column index</li>
51419              * </ul>
51420              * @param {Object} e An edit event (see above for description)
51421              */
51422             "afteredit" : true,
51423             /**
51424              * @event validateedit
51425              * Fires after a cell is edited, but before the value is set in the record. 
51426          * You can use this to modify the value being set in the field, Return false
51427              * to cancel the change. The edit event object has the following properties <br />
51428              * <ul style="padding:5px;padding-left:16px;">
51429          * <li>editor - This editor</li>
51430              * <li>grid - This grid</li>
51431              * <li>record - The record being edited</li>
51432              * <li>field - The field name being edited</li>
51433              * <li>value - The value being set</li>
51434              * <li>originalValue - The original value for the field, before the edit.</li>
51435              * <li>row - The grid row index</li>
51436              * <li>column - The grid column index</li>
51437              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51438              * </ul>
51439              * @param {Object} e An edit event (see above for description)
51440              */
51441             "validateedit" : true
51442         });
51443     this.on("bodyscroll", this.stopEditing,  this);
51444     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51445 };
51446
51447 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51448     /**
51449      * @cfg {Number} clicksToEdit
51450      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51451      */
51452     clicksToEdit: 2,
51453
51454     // private
51455     isEditor : true,
51456     // private
51457     trackMouseOver: false, // causes very odd FF errors
51458
51459     onCellDblClick : function(g, row, col){
51460         this.startEditing(row, col);
51461     },
51462
51463     onEditComplete : function(ed, value, startValue){
51464         this.editing = false;
51465         this.activeEditor = null;
51466         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51467         var r = ed.record;
51468         var field = this.colModel.getDataIndex(ed.col);
51469         var e = {
51470             grid: this,
51471             record: r,
51472             field: field,
51473             originalValue: startValue,
51474             value: value,
51475             row: ed.row,
51476             column: ed.col,
51477             cancel:false,
51478             editor: ed
51479         };
51480         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51481         cell.show();
51482           
51483         if(String(value) !== String(startValue)){
51484             
51485             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51486                 r.set(field, e.value);
51487                 // if we are dealing with a combo box..
51488                 // then we also set the 'name' colum to be the displayField
51489                 if (ed.field.displayField && ed.field.name) {
51490                     r.set(ed.field.name, ed.field.el.dom.value);
51491                 }
51492                 
51493                 delete e.cancel; //?? why!!!
51494                 this.fireEvent("afteredit", e);
51495             }
51496         } else {
51497             this.fireEvent("afteredit", e); // always fire it!
51498         }
51499         this.view.focusCell(ed.row, ed.col);
51500     },
51501
51502     /**
51503      * Starts editing the specified for the specified row/column
51504      * @param {Number} rowIndex
51505      * @param {Number} colIndex
51506      */
51507     startEditing : function(row, col){
51508         this.stopEditing();
51509         if(this.colModel.isCellEditable(col, row)){
51510             this.view.ensureVisible(row, col, true);
51511           
51512             var r = this.dataSource.getAt(row);
51513             var field = this.colModel.getDataIndex(col);
51514             var cell = Roo.get(this.view.getCell(row,col));
51515             var e = {
51516                 grid: this,
51517                 record: r,
51518                 field: field,
51519                 value: r.data[field],
51520                 row: row,
51521                 column: col,
51522                 cancel:false 
51523             };
51524             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51525                 this.editing = true;
51526                 var ed = this.colModel.getCellEditor(col, row);
51527                 
51528                 if (!ed) {
51529                     return;
51530                 }
51531                 if(!ed.rendered){
51532                     ed.render(ed.parentEl || document.body);
51533                 }
51534                 ed.field.reset();
51535                
51536                 cell.hide();
51537                 
51538                 (function(){ // complex but required for focus issues in safari, ie and opera
51539                     ed.row = row;
51540                     ed.col = col;
51541                     ed.record = r;
51542                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51543                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51544                     this.activeEditor = ed;
51545                     var v = r.data[field];
51546                     ed.startEdit(this.view.getCell(row, col), v);
51547                     // combo's with 'displayField and name set
51548                     if (ed.field.displayField && ed.field.name) {
51549                         ed.field.el.dom.value = r.data[ed.field.name];
51550                     }
51551                     
51552                     
51553                 }).defer(50, this);
51554             }
51555         }
51556     },
51557         
51558     /**
51559      * Stops any active editing
51560      */
51561     stopEditing : function(){
51562         if(this.activeEditor){
51563             this.activeEditor.completeEdit();
51564         }
51565         this.activeEditor = null;
51566     }
51567 });/*
51568  * Based on:
51569  * Ext JS Library 1.1.1
51570  * Copyright(c) 2006-2007, Ext JS, LLC.
51571  *
51572  * Originally Released Under LGPL - original licence link has changed is not relivant.
51573  *
51574  * Fork - LGPL
51575  * <script type="text/javascript">
51576  */
51577
51578 // private - not really -- you end up using it !
51579 // This is a support class used internally by the Grid components
51580
51581 /**
51582  * @class Roo.grid.GridEditor
51583  * @extends Roo.Editor
51584  * Class for creating and editable grid elements.
51585  * @param {Object} config any settings (must include field)
51586  */
51587 Roo.grid.GridEditor = function(field, config){
51588     if (!config && field.field) {
51589         config = field;
51590         field = Roo.factory(config.field, Roo.form);
51591     }
51592     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51593     field.monitorTab = false;
51594 };
51595
51596 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51597     
51598     /**
51599      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51600      */
51601     
51602     alignment: "tl-tl",
51603     autoSize: "width",
51604     hideEl : false,
51605     cls: "x-small-editor x-grid-editor",
51606     shim:false,
51607     shadow:"frame"
51608 });/*
51609  * Based on:
51610  * Ext JS Library 1.1.1
51611  * Copyright(c) 2006-2007, Ext JS, LLC.
51612  *
51613  * Originally Released Under LGPL - original licence link has changed is not relivant.
51614  *
51615  * Fork - LGPL
51616  * <script type="text/javascript">
51617  */
51618   
51619
51620   
51621 Roo.grid.PropertyRecord = Roo.data.Record.create([
51622     {name:'name',type:'string'},  'value'
51623 ]);
51624
51625
51626 Roo.grid.PropertyStore = function(grid, source){
51627     this.grid = grid;
51628     this.store = new Roo.data.Store({
51629         recordType : Roo.grid.PropertyRecord
51630     });
51631     this.store.on('update', this.onUpdate,  this);
51632     if(source){
51633         this.setSource(source);
51634     }
51635     Roo.grid.PropertyStore.superclass.constructor.call(this);
51636 };
51637
51638
51639
51640 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51641     setSource : function(o){
51642         this.source = o;
51643         this.store.removeAll();
51644         var data = [];
51645         for(var k in o){
51646             if(this.isEditableValue(o[k])){
51647                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51648             }
51649         }
51650         this.store.loadRecords({records: data}, {}, true);
51651     },
51652
51653     onUpdate : function(ds, record, type){
51654         if(type == Roo.data.Record.EDIT){
51655             var v = record.data['value'];
51656             var oldValue = record.modified['value'];
51657             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51658                 this.source[record.id] = v;
51659                 record.commit();
51660                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51661             }else{
51662                 record.reject();
51663             }
51664         }
51665     },
51666
51667     getProperty : function(row){
51668        return this.store.getAt(row);
51669     },
51670
51671     isEditableValue: function(val){
51672         if(val && val instanceof Date){
51673             return true;
51674         }else if(typeof val == 'object' || typeof val == 'function'){
51675             return false;
51676         }
51677         return true;
51678     },
51679
51680     setValue : function(prop, value){
51681         this.source[prop] = value;
51682         this.store.getById(prop).set('value', value);
51683     },
51684
51685     getSource : function(){
51686         return this.source;
51687     }
51688 });
51689
51690 Roo.grid.PropertyColumnModel = function(grid, store){
51691     this.grid = grid;
51692     var g = Roo.grid;
51693     g.PropertyColumnModel.superclass.constructor.call(this, [
51694         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51695         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51696     ]);
51697     this.store = store;
51698     this.bselect = Roo.DomHelper.append(document.body, {
51699         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51700             {tag: 'option', value: 'true', html: 'true'},
51701             {tag: 'option', value: 'false', html: 'false'}
51702         ]
51703     });
51704     Roo.id(this.bselect);
51705     var f = Roo.form;
51706     this.editors = {
51707         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51708         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51709         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51710         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51711         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51712     };
51713     this.renderCellDelegate = this.renderCell.createDelegate(this);
51714     this.renderPropDelegate = this.renderProp.createDelegate(this);
51715 };
51716
51717 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51718     
51719     
51720     nameText : 'Name',
51721     valueText : 'Value',
51722     
51723     dateFormat : 'm/j/Y',
51724     
51725     
51726     renderDate : function(dateVal){
51727         return dateVal.dateFormat(this.dateFormat);
51728     },
51729
51730     renderBool : function(bVal){
51731         return bVal ? 'true' : 'false';
51732     },
51733
51734     isCellEditable : function(colIndex, rowIndex){
51735         return colIndex == 1;
51736     },
51737
51738     getRenderer : function(col){
51739         return col == 1 ?
51740             this.renderCellDelegate : this.renderPropDelegate;
51741     },
51742
51743     renderProp : function(v){
51744         return this.getPropertyName(v);
51745     },
51746
51747     renderCell : function(val){
51748         var rv = val;
51749         if(val instanceof Date){
51750             rv = this.renderDate(val);
51751         }else if(typeof val == 'boolean'){
51752             rv = this.renderBool(val);
51753         }
51754         return Roo.util.Format.htmlEncode(rv);
51755     },
51756
51757     getPropertyName : function(name){
51758         var pn = this.grid.propertyNames;
51759         return pn && pn[name] ? pn[name] : name;
51760     },
51761
51762     getCellEditor : function(colIndex, rowIndex){
51763         var p = this.store.getProperty(rowIndex);
51764         var n = p.data['name'], val = p.data['value'];
51765         
51766         if(typeof(this.grid.customEditors[n]) == 'string'){
51767             return this.editors[this.grid.customEditors[n]];
51768         }
51769         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51770             return this.grid.customEditors[n];
51771         }
51772         if(val instanceof Date){
51773             return this.editors['date'];
51774         }else if(typeof val == 'number'){
51775             return this.editors['number'];
51776         }else if(typeof val == 'boolean'){
51777             return this.editors['boolean'];
51778         }else{
51779             return this.editors['string'];
51780         }
51781     }
51782 });
51783
51784 /**
51785  * @class Roo.grid.PropertyGrid
51786  * @extends Roo.grid.EditorGrid
51787  * This class represents the  interface of a component based property grid control.
51788  * <br><br>Usage:<pre><code>
51789  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51790       
51791  });
51792  // set any options
51793  grid.render();
51794  * </code></pre>
51795   
51796  * @constructor
51797  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51798  * The container MUST have some type of size defined for the grid to fill. The container will be
51799  * automatically set to position relative if it isn't already.
51800  * @param {Object} config A config object that sets properties on this grid.
51801  */
51802 Roo.grid.PropertyGrid = function(container, config){
51803     config = config || {};
51804     var store = new Roo.grid.PropertyStore(this);
51805     this.store = store;
51806     var cm = new Roo.grid.PropertyColumnModel(this, store);
51807     store.store.sort('name', 'ASC');
51808     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51809         ds: store.store,
51810         cm: cm,
51811         enableColLock:false,
51812         enableColumnMove:false,
51813         stripeRows:false,
51814         trackMouseOver: false,
51815         clicksToEdit:1
51816     }, config));
51817     this.getGridEl().addClass('x-props-grid');
51818     this.lastEditRow = null;
51819     this.on('columnresize', this.onColumnResize, this);
51820     this.addEvents({
51821          /**
51822              * @event beforepropertychange
51823              * Fires before a property changes (return false to stop?)
51824              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51825              * @param {String} id Record Id
51826              * @param {String} newval New Value
51827          * @param {String} oldval Old Value
51828              */
51829         "beforepropertychange": true,
51830         /**
51831              * @event propertychange
51832              * Fires after a property changes
51833              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51834              * @param {String} id Record Id
51835              * @param {String} newval New Value
51836          * @param {String} oldval Old Value
51837              */
51838         "propertychange": true
51839     });
51840     this.customEditors = this.customEditors || {};
51841 };
51842 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51843     
51844      /**
51845      * @cfg {Object} customEditors map of colnames=> custom editors.
51846      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51847      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51848      * false disables editing of the field.
51849          */
51850     
51851       /**
51852      * @cfg {Object} propertyNames map of property Names to their displayed value
51853          */
51854     
51855     render : function(){
51856         Roo.grid.PropertyGrid.superclass.render.call(this);
51857         this.autoSize.defer(100, this);
51858     },
51859
51860     autoSize : function(){
51861         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51862         if(this.view){
51863             this.view.fitColumns();
51864         }
51865     },
51866
51867     onColumnResize : function(){
51868         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51869         this.autoSize();
51870     },
51871     /**
51872      * Sets the data for the Grid
51873      * accepts a Key => Value object of all the elements avaiable.
51874      * @param {Object} data  to appear in grid.
51875      */
51876     setSource : function(source){
51877         this.store.setSource(source);
51878         //this.autoSize();
51879     },
51880     /**
51881      * Gets all the data from the grid.
51882      * @return {Object} data  data stored in grid
51883      */
51884     getSource : function(){
51885         return this.store.getSource();
51886     }
51887 });/*
51888  * Based on:
51889  * Ext JS Library 1.1.1
51890  * Copyright(c) 2006-2007, Ext JS, LLC.
51891  *
51892  * Originally Released Under LGPL - original licence link has changed is not relivant.
51893  *
51894  * Fork - LGPL
51895  * <script type="text/javascript">
51896  */
51897  
51898 /**
51899  * @class Roo.LoadMask
51900  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51901  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51902  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51903  * element's UpdateManager load indicator and will be destroyed after the initial load.
51904  * @constructor
51905  * Create a new LoadMask
51906  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51907  * @param {Object} config The config object
51908  */
51909 Roo.LoadMask = function(el, config){
51910     this.el = Roo.get(el);
51911     Roo.apply(this, config);
51912     if(this.store){
51913         this.store.on('beforeload', this.onBeforeLoad, this);
51914         this.store.on('load', this.onLoad, this);
51915         this.store.on('loadexception', this.onLoad, this);
51916         this.removeMask = false;
51917     }else{
51918         var um = this.el.getUpdateManager();
51919         um.showLoadIndicator = false; // disable the default indicator
51920         um.on('beforeupdate', this.onBeforeLoad, this);
51921         um.on('update', this.onLoad, this);
51922         um.on('failure', this.onLoad, this);
51923         this.removeMask = true;
51924     }
51925 };
51926
51927 Roo.LoadMask.prototype = {
51928     /**
51929      * @cfg {Boolean} removeMask
51930      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51931      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51932      */
51933     /**
51934      * @cfg {String} msg
51935      * The text to display in a centered loading message box (defaults to 'Loading...')
51936      */
51937     msg : 'Loading...',
51938     /**
51939      * @cfg {String} msgCls
51940      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51941      */
51942     msgCls : 'x-mask-loading',
51943
51944     /**
51945      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51946      * @type Boolean
51947      */
51948     disabled: false,
51949
51950     /**
51951      * Disables the mask to prevent it from being displayed
51952      */
51953     disable : function(){
51954        this.disabled = true;
51955     },
51956
51957     /**
51958      * Enables the mask so that it can be displayed
51959      */
51960     enable : function(){
51961         this.disabled = false;
51962     },
51963
51964     // private
51965     onLoad : function(){
51966         this.el.unmask(this.removeMask);
51967     },
51968
51969     // private
51970     onBeforeLoad : function(){
51971         if(!this.disabled){
51972             this.el.mask(this.msg, this.msgCls);
51973         }
51974     },
51975
51976     // private
51977     destroy : function(){
51978         if(this.store){
51979             this.store.un('beforeload', this.onBeforeLoad, this);
51980             this.store.un('load', this.onLoad, this);
51981             this.store.un('loadexception', this.onLoad, this);
51982         }else{
51983             var um = this.el.getUpdateManager();
51984             um.un('beforeupdate', this.onBeforeLoad, this);
51985             um.un('update', this.onLoad, this);
51986             um.un('failure', this.onLoad, this);
51987         }
51988     }
51989 };/*
51990  * Based on:
51991  * Ext JS Library 1.1.1
51992  * Copyright(c) 2006-2007, Ext JS, LLC.
51993  *
51994  * Originally Released Under LGPL - original licence link has changed is not relivant.
51995  *
51996  * Fork - LGPL
51997  * <script type="text/javascript">
51998  */
51999 Roo.XTemplate = function(){
52000     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52001     var s = this.html;
52002
52003     s = ['<tpl>', s, '</tpl>'].join('');
52004
52005     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52006
52007     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52008     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52009     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52010     var m, id = 0;
52011     var tpls = [];
52012
52013     while(m = s.match(re)){
52014        var m2 = m[0].match(nameRe);
52015        var m3 = m[0].match(ifRe);
52016        var m4 = m[0].match(execRe);
52017        var exp = null, fn = null, exec = null;
52018        var name = m2 && m2[1] ? m2[1] : '';
52019        if(m3){
52020            exp = m3 && m3[1] ? m3[1] : null;
52021            if(exp){
52022                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52023            }
52024        }
52025        if(m4){
52026            exp = m4 && m4[1] ? m4[1] : null;
52027            if(exp){
52028                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52029            }
52030        }
52031        if(name){
52032            switch(name){
52033                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52034                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52035                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52036            }
52037        }
52038        tpls.push({
52039             id: id,
52040             target: name,
52041             exec: exec,
52042             test: fn,
52043             body: m[1]||''
52044         });
52045        s = s.replace(m[0], '{xtpl'+ id + '}');
52046        ++id;
52047     }
52048     for(var i = tpls.length-1; i >= 0; --i){
52049         this.compileTpl(tpls[i]);
52050     }
52051     this.master = tpls[tpls.length-1];
52052     this.tpls = tpls;
52053 };
52054 Roo.extend(Roo.XTemplate, Roo.Template, {
52055
52056     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52057
52058     applySubTemplate : function(id, values, parent){
52059         var t = this.tpls[id];
52060         if(t.test && !t.test.call(this, values, parent)){
52061             return '';
52062         }
52063         if(t.exec && t.exec.call(this, values, parent)){
52064             return '';
52065         }
52066         var vs = t.target ? t.target.call(this, values, parent) : values;
52067         parent = t.target ? values : parent;
52068         if(t.target && vs instanceof Array){
52069             var buf = [];
52070             for(var i = 0, len = vs.length; i < len; i++){
52071                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52072             }
52073             return buf.join('');
52074         }
52075         return t.compiled.call(this, vs, parent);
52076     },
52077
52078     compileTpl : function(tpl){
52079         var fm = Roo.util.Format;
52080         var useF = this.disableFormats !== true;
52081         var sep = Roo.isGecko ? "+" : ",";
52082         var fn = function(m, name, format, args){
52083             if(name.substr(0, 4) == 'xtpl'){
52084                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52085             }
52086             var v;
52087             if(name.indexOf('.') != -1){
52088                 v = name;
52089             }else{
52090                 v = "values['" + name + "']";
52091             }
52092             if(format && useF){
52093                 args = args ? ',' + args : "";
52094                 if(format.substr(0, 5) != "this."){
52095                     format = "fm." + format + '(';
52096                 }else{
52097                     format = 'this.call("'+ format.substr(5) + '", ';
52098                     args = ", values";
52099                 }
52100             }else{
52101                 args= ''; format = "("+v+" === undefined ? '' : ";
52102             }
52103             return "'"+ sep + format + v + args + ")"+sep+"'";
52104         };
52105         var body;
52106         // branched to use + in gecko and [].join() in others
52107         if(Roo.isGecko){
52108             body = "tpl.compiled = function(values, parent){ return '" +
52109                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52110                     "';};";
52111         }else{
52112             body = ["tpl.compiled = function(values, parent){ return ['"];
52113             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52114             body.push("'].join('');};");
52115             body = body.join('');
52116         }
52117         /** eval:var:zzzzzzz */
52118         eval(body);
52119         return this;
52120     },
52121
52122     applyTemplate : function(values){
52123         return this.master.compiled.call(this, values, {});
52124         var s = this.subs;
52125     },
52126
52127     apply : function(){
52128         return this.applyTemplate.apply(this, arguments);
52129     },
52130
52131     compile : function(){return this;}
52132 });
52133
52134 Roo.XTemplate.from = function(el){
52135     el = Roo.getDom(el);
52136     return new Roo.XTemplate(el.value || el.innerHTML);
52137 };/*
52138  * Original code for Roojs - LGPL
52139  * <script type="text/javascript">
52140  */
52141  
52142 /**
52143  * @class Roo.XComponent
52144  * A delayed Element creator...
52145  * Or a way to group chunks of interface together.
52146  * 
52147  * Mypart.xyx = new Roo.XComponent({
52148
52149     parent : 'Mypart.xyz', // empty == document.element.!!
52150     order : '001',
52151     name : 'xxxx'
52152     region : 'xxxx'
52153     disabled : function() {} 
52154      
52155     tree : function() { // return an tree of xtype declared components
52156         var MODULE = this;
52157         return 
52158         {
52159             xtype : 'NestedLayoutPanel',
52160             // technicall
52161         }
52162      ]
52163  *})
52164  *
52165  *
52166  * It can be used to build a big heiracy, with parent etc.
52167  * or you can just use this to render a single compoent to a dom element
52168  * MYPART.render(Roo.Element | String(id) | dom_element )
52169  * 
52170  * @extends Roo.util.Observable
52171  * @constructor
52172  * @param cfg {Object} configuration of component
52173  * 
52174  */
52175 Roo.XComponent = function(cfg) {
52176     Roo.apply(this, cfg);
52177     this.addEvents({ 
52178         /**
52179              * @event built
52180              * Fires when this the componnt is built
52181              * @param {Roo.XComponent} c the component
52182              */
52183         'built' : true,
52184         /**
52185              * @event buildcomplete
52186              * Fires on the top level element when all elements have been built
52187              * @param {Roo.XComponent} c the top level component.
52188          */
52189         'buildcomplete' : true
52190         
52191     });
52192     this.region = this.region || 'center'; // default..
52193     Roo.XComponent.register(this);
52194     this.modules = false;
52195     this.el = false; // where the layout goes..
52196     
52197     
52198 }
52199 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52200     /**
52201      * @property el
52202      * The created element (with Roo.factory())
52203      * @type {Roo.Layout}
52204      */
52205     el  : false,
52206     
52207     /**
52208      * @property el
52209      * for BC  - use el in new code
52210      * @type {Roo.Layout}
52211      */
52212     panel : false,
52213     
52214     /**
52215      * @property layout
52216      * for BC  - use el in new code
52217      * @type {Roo.Layout}
52218      */
52219     layout : false,
52220     
52221      /**
52222      * @cfg {Function|boolean} disabled
52223      * If this module is disabled by some rule, return true from the funtion
52224      */
52225     disabled : false,
52226     
52227     /**
52228      * @cfg {String} parent 
52229      * Name of parent element which it get xtype added to..
52230      */
52231     parent: false,
52232     
52233     /**
52234      * @cfg {String} order
52235      * Used to set the order in which elements are created (usefull for multiple tabs)
52236      */
52237     
52238     order : false,
52239     /**
52240      * @cfg {String} name
52241      * String to display while loading.
52242      */
52243     name : false,
52244     /**
52245      * @cfg {String} region
52246      * Region to render component to (defaults to center)
52247      */
52248     region : 'center',
52249     
52250     /**
52251      * @cfg {Array} items
52252      * A single item array - the first element is the root of the tree..
52253      * It's done this way to stay compatible with the Xtype system...
52254      */
52255     items : false,
52256     
52257     
52258      /**
52259      * render
52260      * render element to dom or tree
52261      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52262      */
52263     
52264     render : function(el)
52265     {
52266         
52267         el = el || false;
52268         var hp = this.parent ? 1 : 0;
52269         
52270         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52271             // if parent is a '#.....' string, then let's use that..
52272             var ename = this.parent.substr(1)
52273             this.parent = false;
52274             el = Roo.get(ename);
52275             if (!el) {
52276                 Roo.log("Warning - element can not be found :#" + ename );
52277                 return;
52278             }
52279         }
52280         
52281         
52282         if (!this.parent) {
52283             
52284             el = el ? Roo.get(el) : false;
52285             
52286             // it's a top level one..
52287             this.parent =  {
52288                 el : new Roo.BorderLayout(el || document.body, {
52289                 
52290                      center: {
52291                          titlebar: false,
52292                          autoScroll:false,
52293                          closeOnTab: true,
52294                          tabPosition: 'top',
52295                           //resizeTabs: true,
52296                          alwaysShowTabs: el && hp? false :  true,
52297                          hideTabs: el || !hp ? true :  false,
52298                          minTabWidth: 140
52299                      }
52300                  })
52301             }
52302         }
52303         
52304         
52305             
52306         var tree = this.tree();
52307         tree.region = tree.region || this.region;
52308         this.el = this.parent.el.addxtype(tree);
52309         this.fireEvent('built', this);
52310         
52311         this.panel = this.el;
52312         this.layout = this.panel.layout;    
52313          
52314     }
52315     
52316 });
52317
52318 Roo.apply(Roo.XComponent, {
52319     
52320     /**
52321      * @property  buildCompleted
52322      * True when the builder has completed building the interface.
52323      * @type Boolean
52324      */
52325     buildCompleted : false,
52326      
52327     /**
52328      * @property  topModule
52329      * the upper most module - uses document.element as it's constructor.
52330      * @type Object
52331      */
52332      
52333     topModule  : false,
52334       
52335     /**
52336      * @property  modules
52337      * array of modules to be created by registration system.
52338      * @type {Array} of Roo.XComponent
52339      */
52340     
52341     modules : [],
52342     /**
52343      * @property  elmodules
52344      * array of modules to be created by which use #ID 
52345      * @type {Array} of Roo.XComponent
52346      */
52347      
52348     elmodules : [],
52349
52350     
52351     /**
52352      * Register components to be built later.
52353      *
52354      * This solves the following issues
52355      * - Building is not done on page load, but after an authentication process has occured.
52356      * - Interface elements are registered on page load
52357      * - Parent Interface elements may not be loaded before child, so this handles that..
52358      * 
52359      *
52360      * example:
52361      * 
52362      * MyApp.register({
52363           order : '000001',
52364           module : 'Pman.Tab.projectMgr',
52365           region : 'center',
52366           parent : 'Pman.layout',
52367           disabled : false,  // or use a function..
52368         })
52369      
52370      * * @param {Object} details about module
52371      */
52372     register : function(obj) {
52373         this.modules.push(obj);
52374          
52375     },
52376     /**
52377      * convert a string to an object..
52378      * eg. 'AAA.BBB' -> finds AAA.BBB
52379
52380      */
52381     
52382     toObject : function(str)
52383     {
52384         if (!str || typeof(str) == 'object') {
52385             return str;
52386         }
52387         if (str.substring(0,1) == '#') {
52388             return str;
52389         }
52390
52391         var ar = str.split('.');
52392         var rt, o;
52393         rt = ar.shift();
52394             /** eval:var:o */
52395         try {
52396             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52397         } catch (e) {
52398             throw "Module not found : " + str;
52399         }
52400         
52401         if (o === false) {
52402             throw "Module not found : " + str;
52403         }
52404         Roo.each(ar, function(e) {
52405             if (typeof(o[e]) == 'undefined') {
52406                 throw "Module not found : " + str;
52407             }
52408             o = o[e];
52409         });
52410         
52411         return o;
52412         
52413     },
52414     
52415     
52416     /**
52417      * move modules into their correct place in the tree..
52418      * 
52419      */
52420     preBuild : function ()
52421     {
52422         var _t = this;
52423         Roo.each(this.modules , function (obj)
52424         {
52425             var opar = obj.parent;
52426             try { 
52427                 obj.parent = this.toObject(opar);
52428             } catch(e) {
52429                 Roo.log(e.toString());
52430                 return;
52431             }
52432             
52433             if (!obj.parent) {
52434                 this.topModule = obj;
52435                 return;
52436             }
52437             if (typeof(obj.parent) == 'string') {
52438                 this.elmodules.push(obj);
52439                 return;
52440             }
52441             if (obj.parent.constructor != Roo.XComponent) {
52442                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52443             }
52444             if (!obj.parent.modules) {
52445                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52446                     function(o) { return o.order + '' }
52447                 );
52448             }
52449             
52450             obj.parent.modules.add(obj);
52451         }, this);
52452     },
52453     
52454      /**
52455      * make a list of modules to build.
52456      * @return {Array} list of modules. 
52457      */ 
52458     
52459     buildOrder : function()
52460     {
52461         var _this = this;
52462         var cmp = function(a,b) {   
52463             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52464         };
52465         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52466             throw "No top level modules to build";
52467         }
52468         
52469         // make a flat list in order of modules to build.
52470         var mods = this.topModule ? [ this.topModule ] : [];
52471         Roo.each(this.elmodules,function(e) { mods.push(e) });
52472
52473         
52474         // add modules to their parents..
52475         var addMod = function(m) {
52476            // Roo.debug && Roo.log(m.modKey);
52477             
52478             mods.push(m);
52479             if (m.modules) {
52480                 m.modules.keySort('ASC',  cmp );
52481                 m.modules.each(addMod);
52482             }
52483             // not sure if this is used any more..
52484             if (m.finalize) {
52485                 m.finalize.name = m.name + " (clean up) ";
52486                 mods.push(m.finalize);
52487             }
52488             
52489         }
52490         if (this.topModule) { 
52491             this.topModule.modules.keySort('ASC',  cmp );
52492             this.topModule.modules.each(addMod);
52493         }
52494         return mods;
52495     },
52496     
52497      /**
52498      * Build the registered modules.
52499      * @param {Object} parent element.
52500      * @param {Function} optional method to call after module has been added.
52501      * 
52502      */ 
52503    
52504     build : function() 
52505     {
52506         
52507         this.preBuild();
52508         var mods = this.buildOrder();
52509       
52510         //this.allmods = mods;
52511         //Roo.debug && Roo.log(mods);
52512         //return;
52513         if (!mods.length) { // should not happen
52514             throw "NO modules!!!";
52515         }
52516         
52517         
52518         
52519         // flash it up as modal - so we store the mask!?
52520         Roo.MessageBox.show({ title: 'loading' });
52521         Roo.MessageBox.show({
52522            title: "Please wait...",
52523            msg: "Building Interface...",
52524            width:450,
52525            progress:true,
52526            closable:false,
52527            modal: false
52528           
52529         });
52530         var total = mods.length;
52531         
52532         var _this = this;
52533         var progressRun = function() {
52534             if (!mods.length) {
52535                 Roo.debug && Roo.log('hide?');
52536                 Roo.MessageBox.hide();
52537                 if (_this.topModule) { 
52538                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52539                 }
52540                 // THE END...
52541                 return false;   
52542             }
52543             
52544             var m = mods.shift();
52545             
52546             
52547             Roo.debug && Roo.log(m);
52548             // not sure if this is supported any more.. - modules that are are just function
52549             if (typeof(m) == 'function') { 
52550                 m.call(this);
52551                 return progressRun.defer(10, _this);
52552             } 
52553             
52554             
52555             
52556             Roo.MessageBox.updateProgress(
52557                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52558                     " of " + total + 
52559                     (m.name ? (' - ' + m.name) : '')
52560                     );
52561             
52562          
52563             // is the module disabled?
52564             var disabled = (typeof(m.disabled) == 'function') ?
52565                 m.disabled.call(m.module.disabled) : m.disabled;    
52566             
52567             
52568             if (disabled) {
52569                 return progressRun(); // we do not update the display!
52570             }
52571             
52572             // now build 
52573             
52574             m.render();
52575             // it's 10 on top level, and 1 on others??? why...
52576             return progressRun.defer(10, _this);
52577              
52578         }
52579         progressRun.defer(1, _this);
52580      
52581         
52582         
52583     }
52584     
52585      
52586    
52587     
52588     
52589 });
52590  //<script type="text/javascript">
52591
52592
52593 /**
52594  * @class Roo.Login
52595  * @extends Roo.LayoutDialog
52596  * A generic Login Dialog..... - only one needed in theory!?!?
52597  *
52598  * Fires XComponent builder on success...
52599  * 
52600  * Sends 
52601  *    username,password, lang = for login actions.
52602  *    check = 1 for periodic checking that sesion is valid.
52603  *    passwordRequest = email request password
52604  *    logout = 1 = to logout
52605  * 
52606  * Affects: (this id="????" elements)
52607  *   loading  (removed) (used to indicate application is loading)
52608  *   loading-mask (hides) (used to hide application when it's building loading)
52609  *   
52610  * 
52611  * Usage: 
52612  *    
52613  * 
52614  * Myapp.login = Roo.Login({
52615      url: xxxx,
52616    
52617      realm : 'Myapp', 
52618      
52619      
52620      method : 'POST',
52621      
52622      
52623      * 
52624  })
52625  * 
52626  * 
52627  * 
52628  **/
52629  
52630 Roo.Login = function(cfg)
52631 {
52632     this.addEvents({
52633         'refreshed' : true
52634     });
52635     
52636     Roo.apply(this,cfg);
52637     
52638     Roo.onReady(function() {
52639         this.onLoad();
52640     }, this);
52641     // call parent..
52642     
52643    
52644     Roo.Login.superclass.constructor.call(this, this);
52645     //this.addxtype(this.items[0]);
52646     
52647     
52648 }
52649
52650
52651 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52652     
52653     /**
52654      * @cfg {String} method
52655      * Method used to query for login details.
52656      */
52657     
52658     method : 'POST',
52659     /**
52660      * @cfg {String} url
52661      * URL to query login data. - eg. baseURL + '/Login.php'
52662      */
52663     url : '',
52664     
52665     /**
52666      * @property user
52667      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52668      * @type {Object} 
52669      */
52670     user : false,
52671     /**
52672      * @property checkFails
52673      * Number of times we have attempted to get authentication check, and failed.
52674      * @type {Number} 
52675      */
52676     checkFails : 0,
52677       /**
52678      * @property intervalID
52679      * The window interval that does the constant login checking.
52680      * @type {Number} 
52681      */
52682     intervalID : 0,
52683     
52684     
52685     onLoad : function() // called on page load...
52686     {
52687         // load 
52688          
52689         if (Roo.get('loading')) { // clear any loading indicator..
52690             Roo.get('loading').remove();
52691         }
52692         
52693         //this.switchLang('en'); // set the language to english..
52694        
52695         this.check({
52696             success:  function(response, opts)  {  // check successfull...
52697             
52698                 var res = this.processResponse(response);
52699                 this.checkFails =0;
52700                 if (!res.success) { // error!
52701                     this.checkFails = 5;
52702                     //console.log('call failure');
52703                     return this.failure(response,opts);
52704                 }
52705                 
52706                 if (!res.data.id) { // id=0 == login failure.
52707                     return this.show();
52708                 }
52709                 
52710                               
52711                         //console.log(success);
52712                 this.fillAuth(res.data);   
52713                 this.checkFails =0;
52714                 Roo.XComponent.build();
52715             },
52716             failure : this.show
52717         });
52718         
52719     }, 
52720     
52721     
52722     check: function(cfg) // called every so often to refresh cookie etc..
52723     {
52724         if (cfg.again) { // could be undefined..
52725             this.checkFails++;
52726         } else {
52727             this.checkFails = 0;
52728         }
52729         var _this = this;
52730         if (this.sending) {
52731             if ( this.checkFails > 4) {
52732                 Roo.MessageBox.alert("Error",  
52733                     "Error getting authentication status. - try reloading, or wait a while", function() {
52734                         _this.sending = false;
52735                     }); 
52736                 return;
52737             }
52738             cfg.again = true;
52739             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52740             return;
52741         }
52742         this.sending = true;
52743         
52744         Roo.Ajax.request({  
52745             url: this.url,
52746             params: {
52747                 getAuthUser: true
52748             },  
52749             method: this.method,
52750             success:  cfg.success || this.success,
52751             failure : cfg.failure || this.failure,
52752             scope : this,
52753             callCfg : cfg
52754               
52755         });  
52756     }, 
52757     
52758     
52759     logout: function()
52760     {
52761         window.onbeforeunload = function() { }; // false does not work for IE..
52762         this.user = false;
52763         var _this = this;
52764         
52765         Roo.Ajax.request({  
52766             url: this.url,
52767             params: {
52768                 logout: 1
52769             },  
52770             method: 'GET',
52771             failure : function() {
52772                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52773                     document.location = document.location.toString() + '?ts=' + Math.random();
52774                 });
52775                 
52776             },
52777             success : function() {
52778                 _this.user = false;
52779                 this.checkFails =0;
52780                 // fixme..
52781                 document.location = document.location.toString() + '?ts=' + Math.random();
52782             }
52783               
52784               
52785         }); 
52786     },
52787     
52788     processResponse : function (response)
52789     {
52790         var res = '';
52791         try {
52792             res = Roo.decode(response.responseText);
52793             // oops...
52794             if (typeof(res) != 'object') {
52795                 res = { success : false, errorMsg : res, errors : true };
52796             }
52797             if (typeof(res.success) == 'undefined') {
52798                 res.success = false;
52799             }
52800             
52801         } catch(e) {
52802             res = { success : false,  errorMsg : response.responseText, errors : true };
52803         }
52804         return res;
52805     },
52806     
52807     success : function(response, opts)  // check successfull...
52808     {  
52809         this.sending = false;
52810         var res = this.processResponse(response);
52811         if (!res.success) {
52812             return this.failure(response, opts);
52813         }
52814         if (!res.data || !res.data.id) {
52815             return this.failure(response,opts);
52816         }
52817         //console.log(res);
52818         this.fillAuth(res.data);
52819         
52820         this.checkFails =0;
52821         
52822     },
52823     
52824     
52825     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52826     {
52827         this.authUser = -1;
52828         this.sending = false;
52829         var res = this.processResponse(response);
52830         //console.log(res);
52831         if ( this.checkFails > 2) {
52832         
52833             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52834                 "Error getting authentication status. - try reloading"); 
52835             return;
52836         }
52837         opts.callCfg.again = true;
52838         this.check.defer(1000, this, [ opts.callCfg ]);
52839         return;  
52840     },
52841     
52842     
52843     
52844     fillAuth: function(au) {
52845         this.startAuthCheck();
52846         this.authUserId = au.id;
52847         this.authUser = au;
52848         this.lastChecked = new Date();
52849         this.fireEvent('refreshed', au);
52850         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52851         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52852         au.lang = au.lang || 'en';
52853         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52854         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52855         this.switchLang(au.lang );
52856         
52857      
52858         // open system... - -on setyp..
52859         if (this.authUserId  < 0) {
52860             Roo.MessageBox.alert("Warning", 
52861                 "This is an open system - please set up a admin user with a password.");  
52862         }
52863          
52864         //Pman.onload(); // which should do nothing if it's a re-auth result...
52865         
52866              
52867     },
52868     
52869     startAuthCheck : function() // starter for timeout checking..
52870     {
52871         if (this.intervalID) { // timer already in place...
52872             return false;
52873         }
52874         var _this = this;
52875         this.intervalID =  window.setInterval(function() {
52876               _this.check(false);
52877             }, 120000); // every 120 secs = 2mins..
52878         
52879         
52880     },
52881          
52882     
52883     switchLang : function (lang) 
52884     {
52885         _T = typeof(_T) == 'undefined' ? false : _T;
52886           if (!_T || !lang.length) {
52887             return;
52888         }
52889         
52890         if (!_T && lang != 'en') {
52891             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52892             return;
52893         }
52894         
52895         if (typeof(_T.en) == 'undefined') {
52896             _T.en = {};
52897             Roo.apply(_T.en, _T);
52898         }
52899         
52900         if (typeof(_T[lang]) == 'undefined') {
52901             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52902             return;
52903         }
52904         
52905         
52906         Roo.apply(_T, _T[lang]);
52907         // just need to set the text values for everything...
52908         var _this = this;
52909         /* this will not work ...
52910         if (this.form) { 
52911             
52912                
52913             function formLabel(name, val) {
52914                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52915             }
52916             
52917             formLabel('password', "Password"+':');
52918             formLabel('username', "Email Address"+':');
52919             formLabel('lang', "Language"+':');
52920             this.dialog.setTitle("Login");
52921             this.dialog.buttons[0].setText("Forgot Password");
52922             this.dialog.buttons[1].setText("Login");
52923         }
52924         */
52925         
52926         
52927     },
52928     
52929     
52930     title: "Login",
52931     modal: true,
52932     width:  350,
52933     //height: 230,
52934     height: 180,
52935     shadow: true,
52936     minWidth:200,
52937     minHeight:180,
52938     //proxyDrag: true,
52939     closable: false,
52940     draggable: false,
52941     collapsible: false,
52942     resizable: false,
52943     center: {  // needed??
52944         autoScroll:false,
52945         titlebar: false,
52946        // tabPosition: 'top',
52947         hideTabs: true,
52948         closeOnTab: true,
52949         alwaysShowTabs: false
52950     } ,
52951     listeners : {
52952         
52953         show  : function(dlg)
52954         {
52955             //console.log(this);
52956             this.form = this.layout.getRegion('center').activePanel.form;
52957             this.form.dialog = dlg;
52958             this.buttons[0].form = this.form;
52959             this.buttons[0].dialog = dlg;
52960             this.buttons[1].form = this.form;
52961             this.buttons[1].dialog = dlg;
52962            
52963            //this.resizeToLogo.defer(1000,this);
52964             // this is all related to resizing for logos..
52965             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52966            //// if (!sz) {
52967              //   this.resizeToLogo.defer(1000,this);
52968              //   return;
52969            // }
52970             //var w = Ext.lib.Dom.getViewWidth() - 100;
52971             //var h = Ext.lib.Dom.getViewHeight() - 100;
52972             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52973             //this.center();
52974             if (this.disabled) {
52975                 this.hide();
52976                 return;
52977             }
52978             
52979             if (this.user.id < 0) { // used for inital setup situations.
52980                 return;
52981             }
52982             
52983             if (this.intervalID) {
52984                 // remove the timer
52985                 window.clearInterval(this.intervalID);
52986                 this.intervalID = false;
52987             }
52988             
52989             
52990             if (Roo.get('loading')) {
52991                 Roo.get('loading').remove();
52992             }
52993             if (Roo.get('loading-mask')) {
52994                 Roo.get('loading-mask').hide();
52995             }
52996             
52997             //incomming._node = tnode;
52998             this.form.reset();
52999             //this.dialog.modal = !modal;
53000             //this.dialog.show();
53001             this.el.unmask(); 
53002             
53003             
53004             this.form.setValues({
53005                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53006                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53007             });
53008             
53009             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53010             if (this.form.findField('username').getValue().length > 0 ){
53011                 this.form.findField('password').focus();
53012             } else {
53013                this.form.findField('username').focus();
53014             }
53015     
53016         }
53017     },
53018     items : [
53019          {
53020        
53021             xtype : 'ContentPanel',
53022             xns : Roo,
53023             region: 'center',
53024             fitToFrame : true,
53025             
53026             items : [
53027     
53028                 {
53029                
53030                     xtype : 'Form',
53031                     xns : Roo.form,
53032                     labelWidth: 100,
53033                     style : 'margin: 10px;',
53034                     
53035                     listeners : {
53036                         actionfailed : function(f, act) {
53037                             // form can return { errors: .... }
53038                                 
53039                             //act.result.errors // invalid form element list...
53040                             //act.result.errorMsg// invalid form element list...
53041                             
53042                             this.dialog.el.unmask();
53043                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53044                                         "Login failed - communication error - try again.");
53045                                       
53046                         },
53047                         actioncomplete: function(re, act) {
53048                              
53049                             Roo.state.Manager.set(
53050                                 this.dialog.realm + '.username',  
53051                                     this.findField('username').getValue()
53052                             );
53053                             Roo.state.Manager.set(
53054                                 this.dialog.realm + '.lang',  
53055                                 this.findField('lang').getValue() 
53056                             );
53057                             
53058                             this.dialog.fillAuth(act.result.data);
53059                               
53060                             this.dialog.hide();
53061                             
53062                             if (Roo.get('loading-mask')) {
53063                                 Roo.get('loading-mask').show();
53064                             }
53065                             Roo.XComponent.build();
53066                             
53067                              
53068                             
53069                         }
53070                     },
53071                     items : [
53072                         {
53073                             xtype : 'TextField',
53074                             xns : Roo.form,
53075                             fieldLabel: "Email Address",
53076                             name: 'username',
53077                             width:200,
53078                             autoCreate : {tag: "input", type: "text", size: "20"}
53079                         },
53080                         {
53081                             xtype : 'TextField',
53082                             xns : Roo.form,
53083                             fieldLabel: "Password",
53084                             inputType: 'password',
53085                             name: 'password',
53086                             width:200,
53087                             autoCreate : {tag: "input", type: "text", size: "20"},
53088                             listeners : {
53089                                 specialkey : function(e,ev) {
53090                                     if (ev.keyCode == 13) {
53091                                         this.form.dialog.el.mask("Logging in");
53092                                         this.form.doAction('submit', {
53093                                             url: this.form.dialog.url,
53094                                             method: this.form.dialog.method
53095                                         });
53096                                     }
53097                                 }
53098                             }  
53099                         },
53100                         {
53101                             xtype : 'ComboBox',
53102                             xns : Roo.form,
53103                             fieldLabel: "Language",
53104                             name : 'langdisp',
53105                             store: {
53106                                 xtype : 'SimpleStore',
53107                                 fields: ['lang', 'ldisp'],
53108                                 data : [
53109                                     [ 'en', 'English' ],
53110                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53111                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53112                                 ]
53113                             },
53114                             
53115                             valueField : 'lang',
53116                             hiddenName:  'lang',
53117                             width: 200,
53118                             displayField:'ldisp',
53119                             typeAhead: false,
53120                             editable: false,
53121                             mode: 'local',
53122                             triggerAction: 'all',
53123                             emptyText:'Select a Language...',
53124                             selectOnFocus:true,
53125                             listeners : {
53126                                 select :  function(cb, rec, ix) {
53127                                     this.form.switchLang(rec.data.lang);
53128                                 }
53129                             }
53130                         
53131                         }
53132                     ]
53133                 }
53134                   
53135                 
53136             ]
53137         }
53138     ],
53139     buttons : [
53140         {
53141             xtype : 'Button',
53142             xns : 'Roo',
53143             text : "Forgot Password",
53144             listeners : {
53145                 click : function() {
53146                     //console.log(this);
53147                     var n = this.form.findField('username').getValue();
53148                     if (!n.length) {
53149                         Roo.MessageBox.alert("Error", "Fill in your email address");
53150                         return;
53151                     }
53152                     Roo.Ajax.request({
53153                         url: this.dialog.url,
53154                         params: {
53155                             passwordRequest: n
53156                         },
53157                         method: this.dialog.method,
53158                         success:  function(response, opts)  {  // check successfull...
53159                         
53160                             var res = this.dialog.processResponse(response);
53161                             if (!res.success) { // error!
53162                                Roo.MessageBox.alert("Error" ,
53163                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53164                                return;
53165                             }
53166                             Roo.MessageBox.alert("Notice" ,
53167                                 "Please check you email for the Password Reset message");
53168                         },
53169                         failure : function() {
53170                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53171                         }
53172                         
53173                     });
53174                 }
53175             }
53176         },
53177         {
53178             xtype : 'Button',
53179             xns : 'Roo',
53180             text : "Login",
53181             listeners : {
53182                 
53183                 click : function () {
53184                         
53185                     this.dialog.el.mask("Logging in");
53186                     this.form.doAction('submit', {
53187                             url: this.dialog.url,
53188                             method: this.dialog.method
53189                     });
53190                 }
53191             }
53192         }
53193     ]
53194   
53195   
53196 })
53197  
53198
53199
53200